Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes and tests #1

Merged
merged 7 commits into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@ on:
- main

jobs:
checks:
python:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11"]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install hatch
- name: Run hatch
run: hatch run checks
run: python -m pip install hatch
- name: Run Check
run: |
hatch --version
hatch run default:checks
47 changes: 0 additions & 47 deletions hatch.toml

This file was deleted.

70 changes: 65 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "tagstr"
readme = "README.md"
dynamic = ["version"]
requires-python = ">=3.8"
requires-python = ">=3.9"
description = "Tagged template literals for Python"
license = "MIT"
authors = [
Expand All @@ -18,21 +18,81 @@ keywords = [
[project.urls]
Homepage = "https://github.com/rmorshea/tagstr"

[tool.black]
force-exclude = 'tests/cases/.*.py'

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

# --- Black ----------------------------------------------------------------------------

[tool.black]
force-exclude = 'tests/cases/.*.py'

# --- Coverage -------------------------------------------------------------------------

[tool.coverage.run]
source = ["src/tagstr"]

[tool.coverage.report]
fail_under = 100
# TODO: get 100% coverage
fail_under = 57
show_missing = true
skip_covered = true

# --- Ruff -----------------------------------------------------------------------------

[tool.ruff]
select = ["E", "F", "B", "I"]
extend-exclude = ["tests/cases/*.py"]

# --- Hatch ----------------------------------------------------------------------------

[tool.hatch.version]
path = "src/tagstr/__init__.py"

[tool.hatch.build]
sources = ["src"]

[tool.hatch.build.targets.wheel.force-include]
# When installed in editable mode Hatch inserts a tagstr.pth file that adds
# the project files to the system PATH. The file we include needs to come
# after this alphabetically so that it is loaded after the project is in the
# PATH. This should likely be fixed in Hatch at some point by simply renaming
# the file inserted by Hatch to something like "_tagstr.pth".
# See: https://github.com/pypa/hatch/discussions/827#discussioncomment-5643256
"src/tagstr.pth" = "~tagstr.pth"

[tool.hatch.envs.default.scripts]
checks = [
"hatch run lint:all",
"hatch run test:all",
]
fixes = [
"hatch run lint:fix",
]

[tool.hatch.envs.lint]
skip-install = true
dependencies = ["ruff", "black", "mypy"]

[tool.hatch.envs.lint.scripts]
all = ["style", "types"]
fix = [
"black .",
"ruff check --fix .",
]
style = [
"black --check .",
"ruff check .",
]
types = "mypy --strict src/tagstr"

[tool.hatch.envs.test]
dependencies = ["pytest", "coverage"]

[tool.hatch.envs.test.scripts]
all = ["pass", "cover"]
pass = "pytest tests"
cover = [
"TAGSTR_DISABLE_IMPORT_HOOK=true coverage run -m pytest tests",
"coverage report",
]
2 changes: 1 addition & 1 deletion src/tagstr.pth
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import tagstr
import os; import importlib; os.getenv("TAGSTR_DISABLE_IMPORT_HOOK", "false").lower() != "true" and importlib.import_module("tagstr");
18 changes: 15 additions & 3 deletions src/tagstr/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import os

from tagstr.decorator import tagfunc
from tagstr.utils import Thunk, decode_raw, format_value
from tagstr.types import TagFunc, Thunk
from tagstr.utils import decode_raw, format_value

if os.getenv("TAGSTR_DISABLE_IMPORT_HOOK", "false").lower() != "true":
import tagstr.importer # pragma: no cover # noqa

__version__ = "0.0.1"
__version__ = "0.1.0"

__all__ = ["tagfunc", "Thunk", "decode_raw", "format_value"]
__all__ = [
"decode_raw",
"format_value",
"tagfunc",
"TagFunc",
"Thunk",
]
12 changes: 2 additions & 10 deletions src/tagstr/decorator.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

from typing import Generic, Protocol, TypeVar
from typing import Generic, TypeVar

from tagstr.utils import Thunk
from tagstr.types import TagFunc, Thunk

T = TypeVar("T")

Expand All @@ -19,11 +19,3 @@ def __matmul__(self, other: str) -> T:

def __call__(self, *args: str | Thunk) -> T:
return self.func(*args)


T_co = TypeVar("T_co", covariant=True)


class TagFunc(Protocol[T_co]):
def __call__(self, *args: str | Thunk) -> T_co:
...
23 changes: 23 additions & 0 deletions src/tagstr/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from typing import Any, Callable, Protocol, TypeVar, Union

Thunk = tuple[Callable[[], Any], str, Union[str, None], Union[str, None]]
"""
A "thunk" is a tuple contianing the following:

1. getvalue: a callable that returns the value to be formatted
2. raw: the raw string that was used to create the thunk
3. conv: the conversion character (e.g. "r", "s", "a")
4. formatspec: the format specification (e.g. ".2f")

TODO: thunks likely should like be a namedtuple in the future.
"""


T_co = TypeVar("T_co", covariant=True)


class TagFunc(Protocol[T_co]):
def __call__(self, *args: str | Thunk) -> T_co:
...
51 changes: 22 additions & 29 deletions src/tagstr/utils.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
from typing import Any, Callable, Iterator
from __future__ import annotations

# TODO: thunks likely should have a "tuple-like object with named attributes"
# (so namedtuples) as seen in the os module. This will enable future expansion.
# Of course such named attribute support can also be done in the future!
Thunk = tuple[
Callable[[], Any], # getvalue
str, # raw
str | None, # conv
str | None, # formatspec
]
from typing import Iterator

from tagstr.types import Thunk


def decode_raw(*args: str | Thunk) -> Iterator[str | Thunk]:
Expand All @@ -22,22 +16,21 @@ def decode_raw(*args: str | Thunk) -> Iterator[str | Thunk]:

def format_value(arg: str | Thunk) -> str:
"""Format a value from a thunk or a string."""
match arg:
case str():
return arg
case getvalue, _, conv, spec:
value = getvalue()
match conv:
case "r":
value = repr(value)
case "s":
value = str(value)
case "a":
value = ascii(value)
case None:
pass
case _:
raise ValueError(f"Bad conversion: {conv!r}")
return format(value, spec if spec is not None else "")
case _:
raise ValueError(f"Cannot format {arg!r} - expected a thunk or a string")
if isinstance(arg, str):
return arg
elif isinstance(arg, tuple) and len(arg) == 4:
getvalue, _, conv, spec = arg
value = getvalue()
if conv == "r":
value = repr(value)
elif conv == "s":
value = str(value)
elif conv == "a":
value = ascii(value)
elif conv is None:
pass
else:
raise ValueError(f"Bad conversion: {conv!r}")
return format(value, spec if spec is not None else "")
else:
raise ValueError(f"Cannot format {arg!r} - expected a thunk or a string")
3 changes: 3 additions & 0 deletions temp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import tagstr # noqa

print @ f"asd {1}"