Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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: 10 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,21 @@ jobs:
- "3.12"

steps:
# https://github.com/actions/setup-python
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
# https://github.com/astral-sh/setup-uv
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: pin python version
run: uv python pin ${{ matrix.python-version }}
- name: Install tox and its plugins
run: uv tool install tox --with tox-uv --with tox-gh-actions
- name: Test with tox
run: tox
env:
Expand Down
26 changes: 18 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# .. seealso:: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
[project]
name = "anyconfig"
requires-python = ">=3.8"
requires-python = ">=3.9"
dynamic = [
"version",
]
Expand All @@ -13,7 +13,10 @@ authors = [
maintainers = [
{"name" = "Satoru SATOH", email = "satoru.satoh@gmail.com"},
]
license = { text = "MIT"}
license = "MIT"
license-files = [
"LICENSE.MIT", "AUTHORS.txt"
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
Expand All @@ -28,7 +31,9 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Text Processing :: Markup",
"Topic :: Utilities",
"License :: OSI Approved :: MIT License",
]
dependencies = [
"tox-uv",
]
# .. note:: It requires nothing at minimum.
#dependencies = [
Expand Down Expand Up @@ -131,12 +136,15 @@ ignore = [
"ANN401", # https://docs.astral.sh/ruff/rules/any-type/
"I001", # https://docs.astral.sh/ruff/rules/unsorted-imports/
# https://github.com/PyCQA/isort/issues/2146

"ICN001", # https://docs.astral.sh/ruff/rules/unconventional-import-alias/
"TID252", # https://docs.astral.sh/ruff/rules/relative-imports/

# .. note:: These are disabled until py38 support is dropped.
"UP006", # https://docs.astral.sh/ruff/rules/non-pep585-annotation/
"UP007", # https://docs.astral.sh/ruff/rules/non-pep604-annotation/
"RUF022", # https://docs.astral.sh/ruff/rules/unsorted-dunder-all/#unsorted-dunder-all-ruf022

# .. note:: These are disabled until py39 support is dropped.
"UP007", # https://docs.astral.sh/ruff/rules/non-pep604-annotation-union/
"UP045", # https://docs.astral.sh/ruff/rules/non-pep604-annotation-optional/
]
select = [
"A", # flake8-builtins
Expand Down Expand Up @@ -201,8 +209,10 @@ select = [
#[tool.ruff.lint.flake8-annotations]
#[tool.ruff.lint.flake8-bandit]
#[tool.ruff.lint.flake8-bugbear]
#[tool.ruff.lint.flake8-builtins]

[tool.ruff.lint.flake8-builtins]
ignorelist = [
"open", # for anyconfig.api.open
]

# .. note::
#
Expand Down
31 changes: 21 additions & 10 deletions src/anyconfig/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,38 @@


__all__ = [
"dump", "dumps",
"single_load", "multi_load", "load", "loads",
"open", "version",
"dump", "dumps", # dump APIs.
"single_load", "multi_load", "load", "loads", # load APIs.
"get", "set_", # accessor APIs.
"merge", "open", "version", # other APIs.

# anyconfig.common
"UnknownParserTypeError", "UnknownProcessorTypeError",
"UnknownFileTypeError", "ValidationError",
"UnknownFileTypeError",
"UnknownParserTypeError",
"UnknownProcessorTypeError",
"ValidationError",

# anyconfig.dicsts
"MS_REPLACE", "MS_NO_REPLACE", "MS_DICTS", "MS_DICTS_AND_LISTS",
"MERGE_STRATEGIES", "merge", "get", "set_",
"MERGE_STRATEGIES",
"MS_DICTS",
"MS_DICTS_AND_LISTS",
"MS_NO_REPLACE",
"MS_REPLACE",

# anyconfig.parsers
"load_plugins", "list_types", "list_by_cid", "list_by_type",
"list_by_extension", "findall", "find",
"find",
"findall",
"list_by_cid",
"list_by_extension",
"list_by_type",
"list_types",
"load_plugins",

# anyconfig.query
"try_query",

# anyconfig.validate
"validate", "is_valid", "gen_schema"
"validate", "is_valid", "gen_schema" # validation APIs.
]

# vim:sw=4:ts=4:et:
10 changes: 5 additions & 5 deletions src/anyconfig/api/_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def try_to_load_schema(**options) -> typing.Optional[InDataT]:

:return: Mapping object or None means some errors
"""
ac_schema = options.get("ac_schema", None)
ac_schema = options.get("ac_schema")
if ac_schema is not None:
# Try to detect the appropriate parser to load the schema data as it
# may be different from the original config file's format, perhaps.
Expand Down Expand Up @@ -254,11 +254,11 @@ def multi_load(

if is_dict_like(cups):
dicts_merge(
typing.cast(MappingT, cnf),
typing.cast(MappingT, cups),
typing.cast("MappingT", cnf),
typing.cast("MappingT", cups),
**options
)
dicts_merge(ctx, typing.cast(MappingT, cups), **options)
dicts_merge(ctx, typing.cast("MappingT", cups), **options)
elif len(iois) > 1:
msg = (
f"Object loaded from {ioi!r} is not a mapping object and "
Expand Down Expand Up @@ -364,7 +364,7 @@ def loads(

psr: ParserT = parsers_find(None, forced_type=ac_parser)
schema = None
ac_schema = options.get("ac_schema", None)
ac_schema = options.get("ac_schema")
if ac_schema is not None:
options["ac_schema"] = None
schema = loads(ac_schema, ac_parser=psr, ac_dict=ac_dict,
Expand Down
4 changes: 2 additions & 2 deletions src/anyconfig/api/_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


# pylint: disable=redefined-builtin
def open( # noqa: A001
def open(
path: ioinfo.PathOrIOInfoT,
mode: typing.Optional[str] = None,
ac_parser: parsers.MaybeParserT = None,
Expand Down Expand Up @@ -43,7 +43,7 @@ def open( # noqa: A001
ioi = ioinfo.make(path)
if ioinfo.is_stream(ioi):
warnings.warn(f"Looks already opened stream: {ioi!r}", stacklevel=2)
return typing.cast(typing.IO, ioi.src)
return typing.cast("typing.IO", ioi.src)

psr: ParserT = parsers.find(ioi, forced_type=ac_parser)

Expand Down
4 changes: 2 additions & 2 deletions src/anyconfig/backend/base/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def ropen(cls, filepath: PathOrStrT, **options) -> typing.IO:

:param filepath: Path to file to open to read data
"""
return pathlib.Path(filepath).open( # noqa: SIM115
return pathlib.Path(filepath).open(
cls._open_flags[0], **options
)

Expand All @@ -34,6 +34,6 @@ def wopen(cls, filepath: PathOrStrT, **options) -> typing.IO:

:param filepath: Path to file to open to write data to
"""
return pathlib.Path(filepath).open( # noqa: SIM115
return pathlib.Path(filepath).open(
cls._open_flags[1], **options
)
4 changes: 2 additions & 2 deletions src/anyconfig/backend/base/dumpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def wopen(self, filepath: PathOrStrT, **options) -> typing.IO:
if "encoding" not in options and self._open_write_mode == "w":
options["encoding"] = _ENCODING

return pathlib.Path(filepath).open( # noqa: SIM115
return pathlib.Path(filepath).open(
self._open_write_mode, **options
)

Expand Down Expand Up @@ -112,7 +112,7 @@ def dump(self, cnf: InDataExT, ioi: IoiT, **options) -> None:

if ioinfo.is_stream(ioi):
self.dump_to_stream(
cnf, typing.cast(typing.IO, ioi.src), **options
cnf, typing.cast("typing.IO", ioi.src), **options
)
else:
ensure_outdir_exists(ioi.path)
Expand Down
4 changes: 2 additions & 2 deletions src/anyconfig/backend/base/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def ropen(self, filepath: PathOrStrT, **options) -> typing.IO:
if "encoding" not in options and self._open_read_mode == "r":
options["encoding"] = _ENCODING

return pathlib.Path(filepath).open( # noqa: SIM115
return pathlib.Path(filepath).open(
self._open_read_mode, **options
)

Expand Down Expand Up @@ -202,7 +202,7 @@ def load(

if ioinfo.is_stream(ioi):
cnf = self.load_from_stream(
typing.cast(typing.IO, ioi.src), container, **options
typing.cast("typing.IO", ioi.src), container, **options
)
else:
if ac_ignore_missing and not pathlib.Path(ioi.path).exists():
Expand Down
3 changes: 2 additions & 1 deletion src/anyconfig/backend/ini/configparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ def parse(

if sep in val_s:
return [
parser.parse(typing.cast(str, x)) for x in parser.parse_list(val_s)
parser.parse(typing.cast("str", x))
for x in parser.parse_list(val_s)
]

return parser.parse(val_s)
Expand Down
2 changes: 1 addition & 1 deletion src/anyconfig/backend/python/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def load(self, ioi: IoiT, **options) -> InDataExT:

if ioinfo.is_stream(ioi):
return load_from_temp_file(
typing.cast(typing.IO, ioi.src).read(),
typing.cast("typing.IO", ioi.src).read(),
allow_exec=allow_exec
)

Expand Down
5 changes: 3 additions & 2 deletions src/anyconfig/backend/xml/etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ def _namespaces_from_file(
:return: {namespace_uri: namespace_prefix} or {}
"""
return {
url: prefix for _, (prefix, url)
typing.cast("str", url): typing.cast("tuple[str, str]", prefix)
for _, (prefix, url)
in ElementTree.iterparse(xmlfile, events=("start-ns", ))
}

Expand All @@ -121,7 +122,7 @@ def _tweak_ns(tag: str, **options: dict[str, str]) -> str:
... nspaces={"http://example.com/ns/val/": "val"})
'val:a'
"""
nspaces = options.get("nspaces", None)
nspaces = options.get("nspaces")
if nspaces is not None:
matched = _ET_NS_RE.match(tag)
if matched:
Expand Down
4 changes: 2 additions & 2 deletions src/anyconfig/dicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def _get_update_fn(strategy: str) -> collections.abc.Callable[..., None]:
strategy = MS_DICTS
try:
return typing.cast(
collections.abc.Callable[..., None], _MERGE_FNS[strategy]
"collections.abc.Callable[..., None]", _MERGE_FNS[strategy]
)
except KeyError as exc:
if callable(strategy):
Expand Down Expand Up @@ -294,7 +294,7 @@ def merge(self: DictT, other: UpdatesT, ac_merge: str = MS_DICTS,
else:
try:
iother = typing.cast(
collections.abc.Iterable[tuple[str, typing.Any]],
"collections.abc.Iterable[tuple[str, typing.Any]]",
other
)
for key, val in iother:
Expand Down
5 changes: 5 additions & 0 deletions src/anyconfig/models/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ def __eq__(
"""Test equality."""
return cls.cid() == other.cid()

@classmethod
def __hash__(cls) -> int:
"""Test equality."""
return hash(cls.cid())

def __str__(self) -> str:
"""Provide a string representation."""
return (
Expand Down
8 changes: 4 additions & 4 deletions src/anyconfig/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def attr_val_itr(
:param as_sep: char to separate attributes
"""
for rel in parse_list(str_, as_sep):
rel = typing.cast(str, rel)
rel = typing.cast("str", rel)
if avs_sep not in rel or rel.endswith(avs_sep):
continue

Expand All @@ -91,12 +91,12 @@ def attr_val_itr(
stacklevel=2
)

_attr = typing.cast(str, _attr)
_attr = typing.cast("str", _attr)

if vs_sep in str(_values):
yield (_attr, parse_list(typing.cast(str, _values), vs_sep))
yield (_attr, parse_list(typing.cast("str", _values), vs_sep))
elif _values:
yield (_attr, typing.cast(PrimitiveT, _values))
yield (_attr, typing.cast("PrimitiveT", _values))


def parse_attrlist_0(
Expand Down
2 changes: 1 addition & 1 deletion src/anyconfig/processors/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def list_by_x(
res = [(cid, [prs[cid]]) for cid in sorted(prs.keys())]

elif item in ("type", "extensions"):
res = utils.list_by_x(prs.values(), typing.cast(str, item))
res = utils.list_by_x(prs.values(), typing.cast("str", item))
else:
msg = (
"keyword argument 'item' must be one of "
Expand Down
Loading
Loading