pyproject.toml¶
Source from this local checkout, regenerated when the reader rebuilds.
Line links use #L<number>; a GitHub line range opens its first line.
1 [build-system]2 requires = ["hatchling"]3 build-backend = "hatchling.build"4 5 [project]6 name = "genro-asgi"7 version = "0.46.0"8 description = "Minimal ASGI server core (spec-first redesign of genro-asgi)"9 readme = "README.md"10 requires-python = ">=3.11"11 license = {text = "Apache-2.0"}12 authors = [13 {name = "Genropy Team", email = "info@softwell.it"}14 ]15 keywords = ["asgi", "web", "async", "http", "server"]16 classifiers = [17 "Development Status :: 4 - Beta",18 "Intended Audience :: Developers",19 "License :: OSI Approved :: Apache Software License",20 "Programming Language :: Python :: 3",21 "Programming Language :: Python :: 3.11",22 "Programming Language :: Python :: 3.12",23 "Programming Language :: Python :: 3.13",24 "Topic :: Internet :: WWW/HTTP :: HTTP Servers",25 "Topic :: Software Development :: Libraries :: Python Modules",26 "Framework :: AsyncIO",27 ]28 29 dependencies = [30 "uvicorn>=0.30",31 # uvicorn speaks websocket only with a backend, and none of its own: the32 # server holds websockets, so the backend is a dependency of this package33 # and not of whoever installs it. `websockets` is what is needed, and34 # nothing more — never `uvicorn[standard]`, which drags in what is not.35 "websockets>=13.0",36 "genro-bag>=0.22.0",37 "genro-builders>=0.23.0",38 "genro-toolbox>=0.14.0",39 "genro-tytx>=0.15.0",40 "genro-routes>=0.30.0",41 "genro-storage[encryption]>=0.8.0",42 "pyjwt>=2.0",43 "cryptography>=42.0",44 "httpx>=0.27",45 "psutil>=6.0",46 ]47 48 [project.optional-dependencies]49 test = [50 "pytest>=7.0",51 "pytest-cov>=4.0",52 "pytest-asyncio>=0.23",53 # The suite exercises the TYTX msgpack transport branch of response.py:54 # without this extra that coverage silently vanishes in a clean env.55 "genro-tytx[msgpack]>=0.15.0",56 ]57 dev = [58 "genro-asgi[test]",59 "ruff>=0.1.0",60 "mypy>=1.0",61 ]62 internals = [63 # The reader for the internals/ dossier (mkdocs.yml at the repo root).64 "mkdocs>=1.6",65 "mkdocs-material>=9.6",66 ]67 docs = [68 "sphinx>=7.0",69 "sphinx-rtd-theme>=2.0",70 "sphinx-autodoc-typehints>=2.0",71 "myst-parser>=2.0",72 ]73 74 [project.scripts]75 genro-asgi = "genro_asgi.__main__:main"76 77 [project.urls]78 Homepage = "https://github.com/genropy/genro-asgi"79 Repository = "https://github.com/genropy/genro-asgi"80 "Bug Tracker" = "https://github.com/genropy/genro-asgi/issues"81 82 [tool.hatch.build.targets.wheel]83 packages = ["src/genro_asgi", "src/genro_asgi_multiworker_spa"]84 exclude = ["**/tests/"]85 86 [tool.pytest.ini_options]87 testpaths = ["tests"]88 python_files = "test_*.py"89 python_classes = "Test*"90 python_functions = "test_*"91 addopts = "-v --cov=genro_asgi --cov=genro_asgi_multiworker_spa --cov-report=term-missing"92 asyncio_mode = "auto"93 asyncio_default_fixture_loop_scope = "function"94 95 [tool.ruff]96 line-length = 10097 target-version = "py311"98 99 # Freeze the historical default rule set: ruff 0.16 widened its defaults100 # (isort, naming, RUF/UP families), some of which collide with deliberate101 # idioms here (e.g. implicit-Optional grammar signatures, see the mypy102 # block on config.elements). Adopting new rules is a per-rule decision,103 # not a side effect of whatever version CI happens to install.104 [tool.ruff.lint]105 select = ["E4", "E7", "E9", "F"]106 107 # Fixtures imported from a sibling test module shadow their own name in every108 # test signature that requests them — the pytest idiom ruff reads as F811.109 [tool.ruff.lint.per-file-ignores]110 "tests/spa/orchestration/test_orchestration_cpu_growth.py" = ["F811"]111 "tests/spa/orchestration/test_orchestration_cpu_offload.py" = ["F811"]112 "tests/spa/orchestration/test_orchestration_cpu_temperature_meter.py" = ["F811"]113 "tests/spa/orchestration/test_orchestration_policy_delegation.py" = ["F811"]114 "tests/spa/orchestration/test_orchestration_placement_interval.py" = ["F811"]115 "tests/spa/orchestration/test_orchestration_no_speculative_birth.py" = ["F811"]116 "tests/spa/orchestration/test_orchestration_no_reception_reserve.py" = ["F811"]117 118 # mypy is a non-blocking advisor (parent policy): non-strict, findings119 # reviewed but never gating.120 [tool.mypy]121 python_version = "3.11"122 warn_return_any = true123 warn_unused_configs = true124 exclude = ["tests/", ".*/tests/"]125 126 # The channel client's Optionals are constructor/connect invariants mypy127 # cannot narrow: __init__ parses exactly one address form (uds or tcp), so128 # _tcp is a tuple whenever _uds_path is None; connect() assigns _stream129 # before the receive loop ever reads it. Silence only the two codes this130 # boundary produces, scoped to this module.131 [[tool.mypy.overrides]]132 module = "genro_asgi.channel.client"133 disable_error_code = ["misc", "union-attr"]134 135 # @every marks its wrapper with the declared cadence (every_beats) and reads it136 # back at call time, so a test can move a cadence without touching the caller.137 # functools.wraps returns a _Wrapped mypy models as attribute-less; the pattern is138 # the ordinary function-attribute one, and only that code is silenced here.139 [[tool.mypy.overrides]]140 module = "genro_asgi_multiworker_spa.orchestration.beats"141 disable_error_code = ["attr-defined"]142 143 # CommunicationMixin forwards __call__ cooperatively to the next class in144 # the MRO (mixins compose BEFORE BaseServer, D16). mypy sees the mixin's145 # static superclass as object and calls super().__call__ "undefined",146 # although the composition always provides it at runtime — the dynamic147 # mixin category the parent policy explicitly anticipates.148 [[tool.mypy.overrides]]149 module = "genro_asgi.communication"150 disable_error_code = ["misc"]151 152 # MiddlewareMixin (middleware/__init__.py) forwards __call__ cooperatively153 # to the next class in the MRO — the same dynamic-mixin category as154 # CommunicationMixin above.155 [[tool.mypy.overrides]]156 module = "genro_asgi.middleware"157 disable_error_code = ["misc"]158 159 # SessionMixin forwards __init__ cooperatively to the next class in the MRO160 # (composed BEFORE MiddlewareMixin/BaseServer, D16) — the same dynamic-mixin161 # category as CommunicationMixin: mypy sees the static superclass as object162 # and rejects super().__init__(**kwargs), although the composition always163 # provides it at runtime.164 [[tool.mypy.overrides]]165 module = "genro_asgi.session.mixin"166 disable_error_code = ["misc"]167 168 # AuthMixin forwards __init__ cooperatively (misc, as SessionMixin) and also169 # calls self.session(request) — the §4 contract method supplied by the composed170 # MRO (SessionMixin or BaseServer), which mypy cannot see on the static bases171 # (object). The same dynamic-mixin category the parent policy anticipates.172 [[tool.mypy.overrides]]173 module = "genro_asgi.auth.mixin"174 disable_error_code = ["misc", "attr-defined"]175 176 # ServerApplication's login surface reaches server capabilities supplied by177 # the composed mixins (session_store/promote_session from SessionMixin,178 # user_store wired in Macro 5b) through the statically-typed BaseServer179 # handle (`self.server`/`request.server` -> BaseServer | None) — the same180 # dynamic-mixin category as AuthMixin above: the attributes exist on every181 # runtime composition but not on the static base.182 [[tool.mypy.overrides]]183 module = "genro_asgi.applications.server_app"184 disable_error_code = ["attr-defined", "union-attr"]185 186 # LocalTaskExecutor reaches server.storage (supplied by StorageMixin) and187 # app.route (supplied by RoutedApplication) through statically-typed handles188 # (`server` -> BaseServer, `server.mounts`/`primary` -> BaseApplication) — the189 # same dynamic-mixin category as server_app above: the attributes exist on every190 # runtime composition but not on the static bases.191 [[tool.mypy.overrides]]192 module = "genro_asgi.tasks.executor"193 disable_error_code = ["attr-defined"]194 195 # Grammar element signatures declare what an attribute ACCEPTS, and `= None`196 # means "attribute absent" rather than a value: the annotation is what builders197 # reads into call_args_validations, so implicit Optional is the idiom here —198 # widening to `| None` would change the dtype builders infers from the signature.199 # (`implicit_optional = true` cannot cover this: mypy applies it to plain200 # annotations only, never to explicit unions like `str | BagResolver`.)201 [[tool.mypy.overrides]]202 module = "genro_asgi.config.elements"203 disable_error_code = ["assignment"]204 205 # TaskMixin forwards __init__/__call__ cooperatively to the next class in the206 # MRO (composed BEFORE BaseServer, D16 — same misc category as207 # CommunicationMixin) and builds TaskManager(self): mypy sees the mixin's208 # static self as object/TaskMixin, not the BaseServer the composition always209 # supplies at runtime. The dynamic-mixin category the parent policy anticipates.210 # TaskGrammar's element signatures share config.elements' implicit-Optional211 # idiom (`assignment`, see that block's comment).212 [[tool.mypy.overrides]]213 module = "genro_asgi.tasks.mixin"214 disable_error_code = ["misc", "arg-type", "assignment"]215 216 # TaskManager reaches server.storage / server.session_store — attributes present217 # only when StorageMixin / SessionMixin are composed (which the running server218 # always does), invisible to mypy's static BaseServer. Same attr-defined category219 # as tasks.executor. (scheduler.py stays clean: it goes through the manager.)220 [[tool.mypy.overrides]]221 module = "genro_asgi.tasks.manager"222 disable_error_code = ["attr-defined"]223 224 # TasksSection reaches server.tasks through the static BaseServer | None handle225 # (present only when TaskMixin is composed and a request is in flight) — the226 # same dynamic-composition category as server_app.227 [[tool.mypy.overrides]]228 module = "genro_asgi.applications.server_sections.tasks_section"229 disable_error_code = ["union-attr", "no-any-return"]