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

Enum Support #19

Merged
merged 5 commits into from
Jan 10, 2024
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: 21 additions & 1 deletion src/pytoolconfig/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
from __future__ import annotations

import sys
import warnings
from dataclasses import Field, fields, is_dataclass, replace
from enum import EnumMeta
from pathlib import Path
from typing import TYPE_CHECKING, Any, Generator, Mapping, TypeVar

Expand Down Expand Up @@ -90,6 +92,12 @@ def _fields(dataclass: DataclassInstance | type[DataclassInstance]) -> dict[str,
return {field.name: field for field in fields(dataclass) if field.init}


def _format_enum(option: Any) -> str:
if isinstance(option, str):
return f'"{option}"'
return str(option)


def _dict_to_dataclass(
dataclass: type[T],
dictionary: Mapping[str, Key],
Expand All @@ -103,7 +111,19 @@ def _dict_to_dataclass(
assert isinstance(value, Mapping)
filtered_arg_dict[key_name] = _dict_to_dataclass(sub_table, value)
elif key_name in dataclass_fields:
filtered_arg_dict[key_name] = value
keytype = dataclass_fields[key_name].type
if isinstance(keytype, EnumMeta):
try:
filtered_arg_dict[key_name] = keytype(value)
except ValueError:
valid = set(keytype._value2member_map_.keys())
warnings.warn(
f"{value} is not a valid option for {key_name}, skipping."
f"Valid options are: {','.join(map(_format_enum, valid))}.",
stacklevel=1,
)
else:
filtered_arg_dict[key_name] = value
return dataclass(**filtered_arg_dict)


Expand Down
59 changes: 28 additions & 31 deletions tests/configfiles/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ build-backend = "flit_core.buildapi"

[project]
name = "tomli"
version = "2.0.1" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT
version = "2.0.1" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT
description = "A lil' TOML parser"
authors = [
{ name = "Taneli Hukkinen", email = "hukkin@users.noreply.github.com" },
{ name = "Taneli Hukkinen", email = "hukkin@users.noreply.github.com" },
]
license = { file = "LICENSE" }
requires-python = ">=3.7"
readme = "README.md"
classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries :: Python Modules",
"Typing :: Typed",
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries :: Python Modules",
"Typing :: Typed",
]
keywords = ["toml"]

Expand All @@ -34,6 +34,10 @@ keywords = ["toml"]
"Changelog" = "https://github.com/hukkin/tomli/blob/master/CHANGELOG.md"
[tool.pytoolconfig]
formatter = "black"
[tool.pytoolconfig2]
option2 = true
option1 = false
option3 = "alternate"
[tool.fall_through]
foo_other = "ba"
[tool.isort]
Expand All @@ -50,16 +54,9 @@ profile = "black"


[project.optional-dependencies]
validation = [
"pydantic>=1.7.4",
]
global = [
"appdirs>=1.4.4",
]
doc = [
"tabulate>=0.8.9",
"sphinx>=4.5.0",
]
validation = ["pydantic>=1.7.4"]
global = ["appdirs>=1.4.4"]
doc = ["tabulate>=0.8.9", "sphinx>=4.5.0"]
[tool.tox]
legacy_tox_ini = '''
[tox]
Expand Down Expand Up @@ -135,12 +132,12 @@ source = ['tomli']
[tool.coverage.report]
# Regexes for lines to exclude from consideration
exclude_lines = [
# Re-enable the standard pragma (with extra strictness)
'# pragma: no cover\b',
# Code for static type checkers
'if TYPE_CHECKING:',
# Scripts
'if __name__ == .__main__.:',
# Re-enable the standard pragma (with extra strictness)
'# pragma: no cover\b',
# Code for static type checkers
'if TYPE_CHECKING:',
# Scripts
'if __name__ == .__main__.:',
]


Expand Down
25 changes: 25 additions & 0 deletions tests/test_enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from dataclasses import dataclass
from enum import Enum

from pytoolconfig.pytoolconfig import PyToolConfig


class Demo(Enum):
DISABLED = False
ENABLED = True
ALT = "alternate"


@dataclass
class EnumModel:
option1: Demo = Demo.DISABLED
option2: Demo = Demo.DISABLED
option3: Demo = Demo.DISABLED


def test_simple(cwd):
config = PyToolConfig("pytoolconfig2", cwd, EnumModel)
result = config.parse()
assert result.option1 == Demo.DISABLED
assert result.option2 == Demo.ENABLED
assert result.option3 == Demo.ALT