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

Finish migration to new syntax #39

Merged
merged 3 commits into from
Oct 8, 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
22 changes: 13 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,30 @@ jobs:

runs-on: ubuntu-latest

strategy:
matrix:
directory: ["./type-enum", "./type-enum-plugin"]

defaults:
run:
working-directory: ./type-enum
working-directory: ${{ matrix.directory }}

steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.11'
- name: Install ruff
run: |
python -m pip install --upgrade pip
pip install ruff
- name: Lint with ruff
run: |
ruff check --format=github type_enum
ruff check --output-format=github type_enum*
- name: Format with ruff
run: |
ruff format --check type_enum
ruff format --check type_enum*


run_tests:
Expand All @@ -45,11 +49,11 @@ jobs:
run: pipx install poetry
- uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.11'

- name: Install dependencies
run: |
poetry env use 3.10
poetry env use 3.11
poetry install --no-interaction --no-root
- name: Run tests
run: |
Expand All @@ -68,12 +72,12 @@ jobs:
run: pipx install poetry
- uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.11'

- name: Install dependencies
run: |
poetry env use 3.10
poetry env use 3.11
poetry install --no-interaction --no-root
- name: Run type check
run: |
poetry run mypy tests
poetry run mypy tests --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack
42 changes: 12 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@
```
pip install type-enum
```

And for the mypy plugin:
```
pip install type-enum-plugin
```

Then register the plugin with
```toml
[tool.mypy]
plugins = "type_enum_plugin"
```

### Usage

```python
from type_enum import TypeEnum
from type_enum import Field, TypeEnum

class BgColor(TypeEnum):
transparent = ()
name = (str,)
rgb = (int, int, int)
hsv = (int, int, int)
transparent: Field[()]
name: Field[str]
rgb: Field[int, int, int]
hsv: Field[int, int, int]

background_color: BgColor = BgColor.rgb(39, 127, 168)
assert isinstance(background_color, BgColor)
Expand All @@ -36,28 +43,3 @@ match background_color:
print(f"HSV: {hue}, {saturation}, {value}")
# will print "RGB: 39, 127, 168"
```

You can also specify field names by using a dictionary instead of a tuple:

```python
from type_enum import TypeEnum

class BgColor(TypeEnum):
transparent = ()
name = (str,)
rgb = {"red": int, "green": int, "blue": int} # named args
hsv = {"hue": int, "saturation": int, "value": int}

background_color = BgColor.rgb(red=39, green=127, blue=168)
assert isinstance(background_color, BgColor)

match background_color:
case BgColor.transparent():
print("no color")
case BgColor.name(color_name):
print(f"color name: {color_name}")
case BgColor.rgb(red=r, green=g, blue=b):
print(f"RGB: {r}, {g}, {b}")
case BgColor.hsv(hue=h, saturation=s, value=v):
print(f"HSV: {h}, {s}, {v}")
```
223 changes: 51 additions & 172 deletions type-enum-plugin/poetry.lock

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion type-enum-plugin/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ mypy = "*"

[tool.poetry.group.dev.dependencies]
mypy = "^1.0.1"
ruff = "^0.0.254"
ruff = ">=0.0.292"

[tool.ruff]
line-length = 88
select = [
"I", # isort
]

[tool.ruff.isort]
known-third-party = ["mypy", "pytest"]
extra-standard-library = ["typing_extensions"]
no-lines-before = ["future", "standard-library"]
force-sort-within-sections = true
split-on-trailing-comma = false
classes = ["MDEF"]

[build-system]
requires = ["poetry-core"]
Expand Down
30 changes: 25 additions & 5 deletions type-enum-plugin/type_enum_plugin/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
ProperType,
TupleType,
Type,
TypeAliasType,
TypeOfAny,
TypeType,
TypeVarLikeType,
Expand All @@ -44,8 +43,8 @@
@dataclass(kw_only=True)
class TypeEnumTransform:
cls: ClassDef
# Statement must also be accepted since class definition itself may be passed as the reason
# for subclass/metaclass-based uses of `typing.dataclass_transform`
# Statement must also be accepted since class definition itself may be passed as the
# reason for subclass/metaclass-based uses of `typing.dataclass_transform`
reason: Expression | Statement
# spec: DataclassTransformSpec
api: SemanticAnalyzerPluginInterface
Expand Down Expand Up @@ -113,7 +112,7 @@ def transform(self) -> None:
if stmt.new_syntax:
if not isinstance(stmt.rvalue, TempNode):
self.api.fail(
f"No type annotations allowed in the assignment style", stmt
"No type annotations allowed in the assignment style", stmt
)
error_reported = True
continue
Expand Down Expand Up @@ -170,7 +169,7 @@ def transform(self) -> None:
if aborted:
continue
info = self.create_namedtuple(
lhs.name, [f"_{i}" for i in range(len(types))], types, stmt.line
lhs.name, [f"field{i}" for i in range(len(types))], types, stmt.line
)
if tvars:
info.type_vars = []
Expand Down Expand Up @@ -295,4 +294,25 @@ def type_enum_callback(ctx: ClassDefContext) -> None:

def plugin(version: str) -> type[TypeEnumPlugin]:
# ignore version argument if the plugin works with all mypy versions.
parsed = parse_mypy_version(version)
if parsed < (1, 5):
raise RuntimeError("type-enum-plugin requires mypy >= 1.5.0")
return TypeEnumPlugin


def parse_mypy_version(version: str) -> tuple[int, ...]:
"""Parse mypy string version to tuple of ints.

This function is included here rather than the mypy plugin file because the mypy
plugin file cannot be imported outside a mypy run.

It parses normal version like `0.930` and dev version
like `0.940+dev.04cac4b5d911c4f9529e6ce86a27b44f28846f5d.dirty`.

Args:
version: The mypy version string.

Returns:
A tuple of ints. e.g. (0, 930).
"""
return tuple(map(int, version.partition("+")[0].split(".")))
Loading