Skip to content

Commit e17ff8b

Browse files
remove tox, use hatch environments
1 parent a02d8fe commit e17ff8b

File tree

8 files changed

+86
-64
lines changed

8 files changed

+86
-64
lines changed

.flake8

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[flake8]
2+
max-line-length = 88
3+
extend-ignore = E203

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ __pycache__
44
.pytest_cache/
55
.eggs/
66
.tox/
7-
dist/
7+
dist/

pyproject.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ requires-python = ">=3.7"
1313
authors = [
1414
{ name = "holger krekel" },
1515
]
16+
maintainers = [
17+
{ name = "Ronny Pfannschmidt" , email = "[email protected]"}
18+
]
1619
classifiers = [
1720
"Development Status :: 4 - Beta",
1821
"Intended Audience :: Developers",
@@ -46,3 +49,18 @@ include = [
4649
]
4750
[tool.hatch.build.hooks.vcs]
4851
version-file = "src/apipkg/_version.py"
52+
53+
[tool.hatch.envs.test]
54+
dependencies = [
55+
"coverage[toml]",
56+
"pytest",
57+
"pytest-cov",
58+
]
59+
[[tool.hatch.envs.test.matrix]]
60+
python = ["3.7", "3.8", "3.9", "3.10", "3.11"]
61+
62+
[tool.hatch.envs.test.scripts]
63+
run-coverage = "pytest --cov-config=pyproject.toml --cov=pkg --cov=tests {args}"
64+
run = "run-coverage --no-cov {args}"
65+
[mypy]
66+
python_version = "3.7"

src/apipkg/__init__.py

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
import sys
1212
import threading
1313
from types import ModuleType
14+
from typing import Any
15+
from typing import Callable
16+
from typing import cast
17+
from typing import Iterable
1418

1519
from ._importing import _py_abspath
16-
from ._importing import distribution_version as distribution_version
20+
from ._importing import distribution_version as distribution_version # NOQA:F401
21+
from ._importing import importobj
1722
from ._version import version as __version__ # NOQA:F401
1823

1924
_PRESERVED_MODULE_ATTRS = {
@@ -28,7 +33,12 @@
2833
}
2934

3035

31-
def initpkg(pkgname, exportdefs, attr=None, eager=False):
36+
def initpkg(
37+
pkgname: str,
38+
exportdefs: dict[str, Any],
39+
attr: dict[str, Any] | None = None,
40+
eager: bool = False,
41+
) -> ApiModule:
3242
"""initialize given package from the export definitions."""
3343
attr = attr or {}
3444
mod = sys.modules.get(pkgname)
@@ -39,12 +49,12 @@ def initpkg(pkgname, exportdefs, attr=None, eager=False):
3949
if "bpython" in sys.modules or eager:
4050
for module in list(sys.modules.values()):
4151
if isinstance(module, ApiModule):
42-
module.__dict__
52+
getattr(module, "__dict__")
4353

4454
return mod
4555

4656

47-
def _initpkg(mod, pkgname, exportdefs, attr=None):
57+
def _initpkg(mod: ModuleType | None, pkgname, exportdefs, attr=None) -> ApiModule:
4858
"""Helper for initpkg.
4959
5060
Python 3.3+ uses finer grained locking for imports, and checks sys.modules before
@@ -59,6 +69,7 @@ def _initpkg(mod, pkgname, exportdefs, attr=None):
5969
d.update(attr)
6070
mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
6171
sys.modules[pkgname] = mod
72+
return mod
6273
else:
6374
f = getattr(mod, "__file__", None)
6475
if f:
@@ -74,21 +85,9 @@ def _initpkg(mod, pkgname, exportdefs, attr=None):
7485

7586
# Updating class of existing module as per importlib.util.LazyLoader
7687
mod.__class__ = ApiModule
77-
mod.__init__(pkgname, exportdefs, implprefix=pkgname, attr=attr)
78-
return mod
79-
80-
81-
def importobj(modpath: str, attrname: str) -> object:
82-
"""imports a module, then resolves the attrname on it"""
83-
module = __import__(modpath, None, None, ["__doc__"])
84-
if not attrname:
85-
return module
86-
87-
retval = module
88-
names = attrname.split(".")
89-
for x in names:
90-
retval = getattr(retval, x)
91-
return retval
88+
apimod = cast(ApiModule, mod)
89+
ApiModule.__init__(apimod, pkgname, exportdefs, implprefix=pkgname, attr=attr)
90+
return apimod
9291

9392

9493
def _synchronized(wrapped_function):
@@ -113,16 +112,24 @@ def __docget(self) -> str | None:
113112
return self.__doc
114113
except AttributeError:
115114
if "__doc__" in self.__map__:
116-
return self.__makeattr("__doc__")
115+
return cast(str, self.__makeattr("__doc__"))
117116
else:
118117
return None
119118

120119
def __docset(self, value: str) -> None:
121120
self.__doc = value
122121

123122
__doc__ = property(__docget, __docset) # type: ignore
124-
125-
def __init__(self, name, importspec, implprefix=None, attr=None):
123+
__map__: dict[str, tuple[str, str]]
124+
125+
def __init__(
126+
self,
127+
name: str,
128+
importspec: dict[str, Any],
129+
implprefix: str | None = None,
130+
attr: dict[str, Any] | None = None,
131+
) -> None:
132+
super().__init__(name)
126133
self.__name__ = name
127134
self.__all__ = [x for x in importspec if x != "__onfirstaccess__"]
128135
self.__map__ = {}
@@ -168,7 +175,8 @@ def __makeattr(self, name, isgetattr=False):
168175
target = None
169176
if "__onfirstaccess__" in self.__map__:
170177
target = self.__map__.pop("__onfirstaccess__")
171-
importobj(*target)()
178+
fn = cast(Callable[[], None], importobj(*target))
179+
fn()
172180
try:
173181
modpath, attrname = self.__map__[name]
174182
except KeyError:
@@ -192,53 +200,50 @@ def __makeattr(self, name, isgetattr=False):
192200
else:
193201
result = importobj(modpath, attrname)
194202
setattr(self, name, result)
195-
try:
196-
del self.__map__[name]
197-
except KeyError:
198-
pass # in a recursive-import situation a double-del can happen
203+
# in a recursive-import situation a double-del can happen
204+
self.__map__.pop(name, None)
199205
return result
200206

201207
def __getattr__(self, name):
202208
return self.__makeattr(name, isgetattr=True)
203209

210+
def __dir__(self) -> Iterable[str]:
211+
yield from super().__dir__()
212+
yield from self.__map__
213+
204214
@property
205-
def __dict__(self):
215+
def __dict__(self) -> dict[str, Any]: # type: ignore
206216
# force all the content of the module
207217
# to be loaded when __dict__ is read
208-
dictdescr = ModuleType.__dict__["__dict__"]
209-
dict = dictdescr.__get__(self)
210-
if dict is not None:
218+
dictdescr = ModuleType.__dict__["__dict__"] # type: ignore
219+
ns: dict[str, Any] = dictdescr.__get__(self)
220+
if ns is not None:
211221
hasattr(self, "some")
212222
for name in self.__all__:
213223
try:
214224
self.__makeattr(name)
215225
except AttributeError:
216226
pass
217-
return dict
227+
return ns
218228

219229

220-
def AliasModule(modname: str, modpath: str, attrname: str | None=None):
221-
mod: object | None = None
230+
def AliasModule(modname: str, modpath: str, attrname: str | None = None) -> ModuleType:
231+
cached_obj: object | None = None
222232

223233
def getmod() -> object:
224-
nonlocal mod
225-
if mod is None:
226-
imported = importobj(modpath, None)
227-
if attrname is not None:
228-
mod = getattr(imported, attrname)
229-
else:
230-
mod = imported
231-
232-
return mod
234+
nonlocal cached_obj
235+
if cached_obj is None:
236+
cached_obj = importobj(modpath, attrname)
237+
return cached_obj
233238

234239
x = modpath + ("." + attrname if attrname else "")
235240
repr_result = f"<AliasModule {modname!r} for {x!r}>"
236241

237242
class AliasModule(ModuleType):
238-
def __repr__(self):
243+
def __repr__(self) -> str:
239244
return repr_result
240245

241-
def __getattribute__(self, name):
246+
def __getattribute__(self, name: str) -> object:
242247
try:
243248
return getattr(getmod(), name)
244249
except ImportError:
@@ -248,10 +253,10 @@ def __getattribute__(self, name):
248253
else:
249254
raise
250255

251-
def __setattr__(self, name, value):
256+
def __setattr__(self, name: str, value: object) -> None:
252257
setattr(getmod(), name, value)
253258

254-
def __delattr__(self, name):
259+
def __delattr__(self, name: str) -> None:
255260
delattr(getmod(), name)
256261

257262
return AliasModule(str(modname))

src/apipkg/_alias_module

Whitespace-only changes.

src/apipkg/_importing.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
23
import os
34
import sys
45

@@ -27,3 +28,14 @@ def distribution_version(name: str) -> str | None:
2728
return None
2829

2930

31+
def importobj(modpath: str, attrname: str | None) -> object:
32+
"""imports a module, then resolves the attrname on it"""
33+
module = __import__(modpath, None, None, ["__doc__"])
34+
if not attrname:
35+
return module
36+
37+
retval = module
38+
names = attrname.split(".")
39+
for x in names:
40+
retval = getattr(retval, x)
41+
return retval

src/apipkg/py.typed

Whitespace-only changes.

tox.ini

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)