engin 0.0.16__tar.gz → 0.0.18__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. {engin-0.0.16 → engin-0.0.18}/CHANGELOG.md +20 -0
  2. {engin-0.0.16 → engin-0.0.18}/PKG-INFO +1 -1
  3. {engin-0.0.16 → engin-0.0.18}/pyproject.toml +1 -1
  4. {engin-0.0.16 → engin-0.0.18}/src/engin/_assembler.py +7 -2
  5. {engin-0.0.16 → engin-0.0.18}/src/engin/_cli/__init__.py +11 -0
  6. engin-0.0.18/src/engin/_cli/_common.py +51 -0
  7. {engin-0.0.16 → engin-0.0.18}/src/engin/_cli/_graph.py +5 -36
  8. engin-0.0.18/src/engin/_cli/_inspect.py +94 -0
  9. {engin-0.0.16 → engin-0.0.18}/src/engin/_dependency.py +28 -18
  10. {engin-0.0.16 → engin-0.0.18}/src/engin/_graph.py +1 -1
  11. {engin-0.0.16 → engin-0.0.18}/src/engin/ext/fastapi.py +2 -2
  12. {engin-0.0.16 → engin-0.0.18}/tests/cli/test_graph.py +1 -1
  13. engin-0.0.18/tests/cli/test_inspect.py +13 -0
  14. {engin-0.0.16 → engin-0.0.18}/tests/test_dependencies.py +12 -0
  15. {engin-0.0.16 → engin-0.0.18}/uv.lock +50 -49
  16. engin-0.0.16/src/engin/_cli/_utils.py +0 -18
  17. {engin-0.0.16 → engin-0.0.18}/.github/workflows/check.yaml +0 -0
  18. {engin-0.0.16 → engin-0.0.18}/.github/workflows/publish.yaml +0 -0
  19. {engin-0.0.16 → engin-0.0.18}/.gitignore +0 -0
  20. {engin-0.0.16 → engin-0.0.18}/.readthedocs.yaml +0 -0
  21. {engin-0.0.16 → engin-0.0.18}/LICENSE +0 -0
  22. {engin-0.0.16 → engin-0.0.18}/README.md +0 -0
  23. {engin-0.0.16 → engin-0.0.18}/docs/concepts/engin.md +0 -0
  24. {engin-0.0.16 → engin-0.0.18}/docs/concepts/invocations.md +0 -0
  25. {engin-0.0.16 → engin-0.0.18}/docs/concepts/lifecycle.md +0 -0
  26. {engin-0.0.16 → engin-0.0.18}/docs/concepts/providers.md +0 -0
  27. {engin-0.0.16 → engin-0.0.18}/docs/getting-started.md +0 -0
  28. {engin-0.0.16 → engin-0.0.18}/docs/guides/fastapi-graph.png +0 -0
  29. {engin-0.0.16 → engin-0.0.18}/docs/guides/fastapi.md +0 -0
  30. {engin-0.0.16 → engin-0.0.18}/docs/index.md +0 -0
  31. {engin-0.0.16 → engin-0.0.18}/docs/js/readthedocs.js +0 -0
  32. {engin-0.0.16 → engin-0.0.18}/docs/overrides/main.html +0 -0
  33. {engin-0.0.16 → engin-0.0.18}/docs/reference.md +0 -0
  34. {engin-0.0.16 → engin-0.0.18}/examples/__init__.py +0 -0
  35. {engin-0.0.16 → engin-0.0.18}/examples/asgi/__init__.py +0 -0
  36. {engin-0.0.16 → engin-0.0.18}/examples/asgi/app.py +0 -0
  37. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/__init__.py +0 -0
  38. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/db/__init__.py +0 -0
  39. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/db/adapaters/__init__.py +0 -0
  40. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/db/adapaters/memory.py +0 -0
  41. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/db/block.py +0 -0
  42. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/db/ports.py +0 -0
  43. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/starlette/__init__.py +0 -0
  44. {engin-0.0.16 → engin-0.0.18}/examples/asgi/common/starlette/endpoint.py +0 -0
  45. {engin-0.0.16 → engin-0.0.18}/examples/asgi/features/__init__.py +0 -0
  46. {engin-0.0.16 → engin-0.0.18}/examples/asgi/features/cats/__init__.py +0 -0
  47. {engin-0.0.16 → engin-0.0.18}/examples/asgi/features/cats/api/__init__.py +0 -0
  48. {engin-0.0.16 → engin-0.0.18}/examples/asgi/features/cats/api/get.py +0 -0
  49. {engin-0.0.16 → engin-0.0.18}/examples/asgi/features/cats/api/post.py +0 -0
  50. {engin-0.0.16 → engin-0.0.18}/examples/asgi/features/cats/block.py +0 -0
  51. {engin-0.0.16 → engin-0.0.18}/examples/asgi/features/cats/domain.py +0 -0
  52. {engin-0.0.16 → engin-0.0.18}/examples/asgi/main.py +0 -0
  53. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/__init__.py +0 -0
  54. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/app.py +0 -0
  55. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/main.py +0 -0
  56. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/__init__.py +0 -0
  57. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/cats/__init__.py +0 -0
  58. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/cats/adapters/__init__.py +0 -0
  59. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/cats/adapters/repository.py +0 -0
  60. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/cats/api.py +0 -0
  61. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/cats/block.py +0 -0
  62. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/cats/domain.py +0 -0
  63. {engin-0.0.16 → engin-0.0.18}/examples/fastapi/routes/cats/ports.py +0 -0
  64. {engin-0.0.16 → engin-0.0.18}/examples/simple/__init__.py +0 -0
  65. {engin-0.0.16 → engin-0.0.18}/examples/simple/main.py +0 -0
  66. {engin-0.0.16 → engin-0.0.18}/mkdocs.yaml +0 -0
  67. {engin-0.0.16 → engin-0.0.18}/src/engin/__init__.py +0 -0
  68. {engin-0.0.16 → engin-0.0.18}/src/engin/_block.py +0 -0
  69. {engin-0.0.16 → engin-0.0.18}/src/engin/_cli/_graph.html +0 -0
  70. {engin-0.0.16 → engin-0.0.18}/src/engin/_engin.py +0 -0
  71. {engin-0.0.16 → engin-0.0.18}/src/engin/_exceptions.py +0 -0
  72. {engin-0.0.16 → engin-0.0.18}/src/engin/_introspect.py +0 -0
  73. {engin-0.0.16 → engin-0.0.18}/src/engin/_lifecycle.py +0 -0
  74. {engin-0.0.16 → engin-0.0.18}/src/engin/_option.py +0 -0
  75. {engin-0.0.16 → engin-0.0.18}/src/engin/_type_utils.py +0 -0
  76. {engin-0.0.16 → engin-0.0.18}/src/engin/ext/__init__.py +0 -0
  77. {engin-0.0.16 → engin-0.0.18}/src/engin/ext/asgi.py +0 -0
  78. {engin-0.0.16 → engin-0.0.18}/src/engin/py.typed +0 -0
  79. {engin-0.0.16 → engin-0.0.18}/tests/__init__.py +0 -0
  80. {engin-0.0.16 → engin-0.0.18}/tests/acceptance/__init__.py +0 -0
  81. {engin-0.0.16 → engin-0.0.18}/tests/acceptance/test_error_in_shutdown.py +0 -0
  82. {engin-0.0.16 → engin-0.0.18}/tests/acceptance/test_error_in_start_up.py +0 -0
  83. {engin-0.0.16 → engin-0.0.18}/tests/acceptance/test_fastapi.py +0 -0
  84. {engin-0.0.16 → engin-0.0.18}/tests/cli/__init__.py +0 -0
  85. {engin-0.0.16 → engin-0.0.18}/tests/conftest.py +0 -0
  86. {engin-0.0.16 → engin-0.0.18}/tests/deps.py +0 -0
  87. {engin-0.0.16 → engin-0.0.18}/tests/test_assembler.py +0 -0
  88. {engin-0.0.16 → engin-0.0.18}/tests/test_block.py +0 -0
  89. {engin-0.0.16 → engin-0.0.18}/tests/test_engin.py +0 -0
  90. {engin-0.0.16 → engin-0.0.18}/tests/test_graph.py +0 -0
  91. {engin-0.0.16 → engin-0.0.18}/tests/test_lifecycle.py +0 -0
  92. {engin-0.0.16 → engin-0.0.18}/tests/test_utils.py +0 -0
@@ -6,6 +6,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
+ ## [0.0.18] - 2025-04-25
10
+
11
+ ### Added
12
+
13
+ - A new cli option `engin inspect` that can be used to inspect providers, e.g.
14
+ `engin inspect examples.simple.main:engin --module httpx`
15
+
16
+
17
+ ## [0.0.17] - 2025-04-20
18
+
19
+ ### Added
20
+
21
+ - `Provide` now has the `as_type` parameter that `Supply` had previously.
22
+
23
+ ### Changed
24
+
25
+ - Renamed `parameter_types` property on dependencies to `parameter_type_ids` to be more
26
+ explicit.
27
+
28
+
9
29
  ## [0.0.16] - 2025-04-16
10
30
 
11
31
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engin
3
- Version: 0.0.16
3
+ Version: 0.0.18
4
4
  Summary: An async-first modular application framework
5
5
  Project-URL: Homepage, https://github.com/invokermain/engin
6
6
  Project-URL: Documentation, https://engin.readthedocs.io/en/latest/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "engin"
3
- version = "0.0.16"
3
+ version = "0.0.18"
4
4
  description = "An async-first modular application framework"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import logging
3
3
  from collections import defaultdict
4
- from collections.abc import Iterable
4
+ from collections.abc import Iterable, Sequence
5
5
  from contextvars import ContextVar
6
6
  from dataclasses import dataclass
7
7
  from inspect import BoundArguments, Signature
@@ -75,6 +75,11 @@ class Assembler:
75
75
  else:
76
76
  self._multiproviders[type_id].append(provider)
77
77
 
78
+ @property
79
+ def providers(self) -> Sequence[Provide[Any]]:
80
+ multi_providers = [p for multi in self._multiproviders.values() for p in multi]
81
+ return [*self._providers.values(), *multi_providers]
82
+
78
83
  async def assemble(self, dependency: Dependency[Any, T]) -> AssembledDependency[T]:
79
84
  """
80
85
  Assemble a dependency.
@@ -228,7 +233,7 @@ class Assembler:
228
233
  yield from (
229
234
  child_provider
230
235
  for root_provider in root_providers
231
- for root_provider_param in root_provider.parameter_types
236
+ for root_provider_param in root_provider.parameter_type_ids
232
237
  for child_provider in self._resolve_providers(root_provider_param)
233
238
  )
234
239
  yield from root_providers
@@ -1,3 +1,6 @@
1
+ import logging
2
+ import sys
3
+
1
4
  try:
2
5
  import typer
3
6
  except ImportError:
@@ -7,7 +10,15 @@ except ImportError:
7
10
  ) from None
8
11
 
9
12
  from engin._cli._graph import cli as graph_cli
13
+ from engin._cli._inspect import cli as inspect_cli
14
+
15
+ # mute logging from importing of files + engin's debug logging.
16
+ logging.disable()
17
+
18
+ # add cwd to path to enable local package imports
19
+ sys.path.insert(0, "")
10
20
 
11
21
  app = typer.Typer()
12
22
 
13
23
  app.add_typer(graph_cli)
24
+ app.add_typer(inspect_cli)
@@ -0,0 +1,51 @@
1
+ import importlib
2
+ from typing import Never
3
+
4
+ import typer
5
+ from rich import print
6
+ from rich.panel import Panel
7
+
8
+ from engin import Engin
9
+
10
+
11
+ def print_error(msg: str) -> Never:
12
+ print(
13
+ Panel(
14
+ title="Error",
15
+ renderable=msg,
16
+ title_align="left",
17
+ border_style="red",
18
+ highlight=True,
19
+ )
20
+ )
21
+ raise typer.Exit(code=1)
22
+
23
+
24
+ COMMON_HELP = {
25
+ "app": (
26
+ "The import path of your Engin instance, in the form 'package:application'"
27
+ ", e.g. 'app.main:engin'"
28
+ )
29
+ }
30
+
31
+
32
+ def get_engin_instance(app: str) -> tuple[str, str, Engin]:
33
+ try:
34
+ module_name, engin_name = app.split(":", maxsplit=1)
35
+ except ValueError:
36
+ print_error("Expected an argument of the form 'module:attribute', e.g. 'myapp:engin'")
37
+
38
+ try:
39
+ module = importlib.import_module(module_name)
40
+ except ModuleNotFoundError:
41
+ print_error(f"Unable to find module '{module_name}'")
42
+
43
+ try:
44
+ instance = getattr(module, engin_name)
45
+ except AttributeError:
46
+ print_error(f"Module '{module_name}' has no attribute '{engin_name}'")
47
+
48
+ if not isinstance(instance, Engin):
49
+ print_error(f"'{app}' is not an Engin instance")
50
+
51
+ return module_name, engin_name, instance
@@ -1,8 +1,5 @@
1
1
  import contextlib
2
- import importlib
3
- import logging
4
2
  import socketserver
5
- import sys
6
3
  import threading
7
4
  from http.server import BaseHTTPRequestHandler
8
5
  from pathlib import Path
@@ -12,8 +9,8 @@ from typing import Annotated, Any
12
9
  import typer
13
10
  from rich import print
14
11
 
15
- from engin import Engin, Entrypoint, Invoke, TypeId
16
- from engin._cli._utils import print_error
12
+ from engin import Entrypoint, Invoke, TypeId
13
+ from engin._cli._common import COMMON_HELP, get_engin_instance
17
14
  from engin._dependency import Dependency, Provide, Supply
18
15
  from engin.ext.asgi import ASGIEngin
19
16
 
@@ -24,53 +21,25 @@ except ImportError:
24
21
 
25
22
  cli = typer.Typer()
26
23
 
27
- # mute logging from importing of files + engin's debug logging.
28
- logging.disable()
29
24
 
30
25
  _APP_ORIGIN = ""
31
26
 
32
- _CLI_HELP = {
33
- "app": (
34
- "The import path of your Engin instance, in the form 'package:application'"
35
- ", e.g. 'app.main:engin'"
36
- )
37
- }
38
-
39
27
 
40
28
  @cli.command(name="graph")
41
29
  def serve_graph(
42
30
  app: Annotated[
43
31
  str,
44
- typer.Argument(help=_CLI_HELP["app"]),
32
+ typer.Argument(help=COMMON_HELP["app"]),
45
33
  ],
46
34
  ) -> None:
47
35
  """
48
36
  Creates a visualisation of your application's dependencies.
49
37
  """
50
- # add cwd to path to enable local package imports
51
- sys.path.insert(0, "")
52
-
53
- try:
54
- module_name, engin_name = app.split(":", maxsplit=1)
55
- except ValueError:
56
- print_error("Expected an argument of the form 'module:attribute', e.g. 'myapp:engin'")
38
+ module_name, _, instance = get_engin_instance(app)
57
39
 
58
40
  global _APP_ORIGIN
59
41
  _APP_ORIGIN = module_name.split(".", maxsplit=1)[0]
60
42
 
61
- try:
62
- module = importlib.import_module(module_name)
63
- except ModuleNotFoundError:
64
- print_error(f"unable to find module '{module_name}'")
65
-
66
- try:
67
- instance = getattr(module, engin_name)
68
- except AttributeError:
69
- print_error(f"module '{module_name}' has no attribute '{engin_name}'")
70
-
71
- if not isinstance(instance, Engin):
72
- print_error(f"'{app}' is not an Engin instance")
73
-
74
43
  nodes = instance.graph()
75
44
 
76
45
  # transform dependencies into mermaid syntax
@@ -165,7 +134,7 @@ def _render_node(node: Dependency, render_block: bool = True) -> str:
165
134
  md += f"{_short_name(node.return_type_id)}"
166
135
  return f'{node_id}["`{md}`"]{style}'
167
136
  if isinstance(node, Entrypoint):
168
- entrypoint_type = node.parameter_types[0]
137
+ entrypoint_type = node.parameter_type_ids[0]
169
138
  md += f"{entrypoint_type}"
170
139
  return f'{node_id}[/"`{md}`"\\]{style}'
171
140
  if isinstance(node, Invoke):
@@ -0,0 +1,94 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+ from rich import box
5
+ from rich.console import Console
6
+ from rich.table import Table
7
+
8
+ from engin import Supply
9
+ from engin._cli._common import COMMON_HELP, get_engin_instance, print_error
10
+
11
+ cli = typer.Typer()
12
+ _CLI_HELP = {
13
+ "type": "Filter providers by the provided type, e.g. `AsyncClient` or `float[]`",
14
+ "module": "Filter providers by the provided types' module, e.g. `engin` or `httpx`",
15
+ "verbose": "Enables verbose output",
16
+ }
17
+
18
+
19
+ @cli.command(name="inspect")
20
+ def serve_graph(
21
+ app: Annotated[
22
+ str,
23
+ typer.Argument(help=COMMON_HELP["app"]),
24
+ ],
25
+ type_: Annotated[
26
+ str | None,
27
+ typer.Option("--type", help=_CLI_HELP["type"]),
28
+ ] = None,
29
+ module: Annotated[
30
+ str | None,
31
+ typer.Option(help=_CLI_HELP["module"]),
32
+ ] = None,
33
+ verbose: Annotated[
34
+ bool, typer.Option("--verbose", "-v", help=_CLI_HELP["verbose"])
35
+ ] = False,
36
+ ) -> None:
37
+ """
38
+ Shows metadata for all matching providers.
39
+
40
+ Examples:
41
+
42
+ 1. `engin inspect examples.simple.main:engin --module httpx`
43
+
44
+ 2. `engin inspect examples.simple.main:engin --type AsyncClient`
45
+ """
46
+ module_name, _, instance = get_engin_instance(app)
47
+
48
+ console = Console()
49
+
50
+ providers = []
51
+ for provider in instance.assembler.providers:
52
+ type_id = provider.return_type_id
53
+ if type_ is not None:
54
+ type_name = str(type_id).rsplit(".", maxsplit=1)[-1]
55
+ if type_ != type_name:
56
+ if verbose:
57
+ console.print(
58
+ f"Ignoring '{provider.return_type_id}' as `{type_} != {type_name}",
59
+ style="dim",
60
+ )
61
+ continue
62
+ if module is not None:
63
+ module_name = str(type_id).split(".", maxsplit=1)[0]
64
+ if module != module_name:
65
+ if verbose:
66
+ console.print(
67
+ f"Ignoring '{provider.return_type_id}' as `{module} != {module_name}",
68
+ style="dim",
69
+ )
70
+ continue
71
+ providers.append(provider)
72
+
73
+ matching_provider_count = len(providers)
74
+ if matching_provider_count == 0:
75
+ available = sorted(map(str, instance.assembler.providers))
76
+ print_error(f"No matching providers, available: {available}")
77
+
78
+ if matching_provider_count > 1:
79
+ console.print(f"Found {matching_provider_count} matching providers", style="dim")
80
+
81
+ table = Table(show_header=False, show_lines=False, box=box.ASCII)
82
+
83
+ for provider in sorted(providers, key=lambda p: p.source_module):
84
+ is_supply = isinstance(provider, Supply)
85
+
86
+ table.add_row("name", str(provider), style="bold", end_section=True)
87
+ table.add_row("scope", provider.scope or "N/A")
88
+ table.add_row("func", provider.func_name if not is_supply else "N/A")
89
+ table.add_row("block", provider.block_name or "N/A")
90
+ table.add_row("source module", provider.source_module or "N/A")
91
+ table.add_row("source package", provider.source_package or "N/A")
92
+ table.add_section()
93
+
94
+ console.print(table)
@@ -76,7 +76,7 @@ class Dependency(ABC, Option, Generic[P, T]):
76
76
  return f"{self._func.__module__}.{self._func.__name__}"
77
77
 
78
78
  @property
79
- def parameter_types(self) -> list[TypeId]:
79
+ def parameter_type_ids(self) -> list[TypeId]:
80
80
  parameters = list(self._signature.parameters.values())
81
81
  if not parameters:
82
82
  return []
@@ -136,7 +136,7 @@ class Entrypoint(Invoke):
136
136
  super().__init__(invocation=_noop)
137
137
 
138
138
  @property
139
- def parameter_types(self) -> list[TypeId]:
139
+ def parameter_type_ids(self) -> list[TypeId]:
140
140
  return [TypeId.from_type(self._type)]
141
141
 
142
142
  @property
@@ -153,7 +153,12 @@ class Entrypoint(Invoke):
153
153
 
154
154
  class Provide(Dependency[Any, T]):
155
155
  def __init__(
156
- self, builder: Func[P, T], *, scope: str | None = None, override: bool = False
156
+ self,
157
+ builder: Func[P, T],
158
+ *,
159
+ scope: str | None = None,
160
+ as_type: type | None = None,
161
+ override: bool = False,
157
162
  ) -> None:
158
163
  """
159
164
  Provide a type via a builder or factory function.
@@ -161,19 +166,26 @@ class Provide(Dependency[Any, T]):
161
166
  Args:
162
167
  builder: the builder function that returns the type.
163
168
  scope: (optional) associate this provider with a specific scope.
164
- override: (optional) allow this provider to override existing providers from
165
- the same package.
169
+ as_type: (optional) allows you to explicitly specify the provided type, e.g.
170
+ to type erase a concrete type, or to provide a mock implementation.
171
+ override: (optional) allow this provider to override other providers for the
172
+ same type from the same package.
166
173
  """
167
174
  super().__init__(func=builder)
168
175
  self._scope = scope
169
176
  self._override = override
177
+ self._explicit_type = as_type
178
+
179
+ if self._explicit_type is not None:
180
+ self._signature = self._signature.replace(return_annotation=self._explicit_type)
181
+
170
182
  self._is_multi = typing.get_origin(self.return_type) is list
171
183
 
172
184
  # Validate that the provider does to depend on its own output value, as this will
173
185
  # cause a recursion error and is undefined behaviour wise.
174
186
  if any(
175
187
  self.return_type == param.annotation
176
- for param in self.signature.parameters.values()
188
+ for param in self._signature.parameters.values()
177
189
  ):
178
190
  raise ValueError("A provider cannot depend on its own return type")
179
191
 
@@ -187,6 +199,8 @@ class Provide(Dependency[Any, T]):
187
199
 
188
200
  @property
189
201
  def return_type(self) -> type[T]:
202
+ if self._explicit_type is not None:
203
+ return self._explicit_type
190
204
  if isclass(self._func):
191
205
  return_type = self._func # __init__ returns self
192
206
  else:
@@ -253,23 +267,19 @@ class Supply(Provide, Generic[T]):
253
267
  function.
254
268
 
255
269
  Args:
256
- value: the value to Supply
257
- as_type: allows you to specify the provided type, useful for type erasing,
258
- e.g. Supply a concrete value but specify it as an interface or other
259
- abstraction.
260
- override: allow this provider to override existing providers from the same
261
- package.
270
+ value: the value to Supply.
271
+ as_type: (optional) allows you to explicitly specify the provided type, e.g.
272
+ to type erase a concrete type, or to provide a mock implementation.
273
+ override: (optional) allow this provider to override other providers for the
274
+ same type from the same package.
262
275
  """
263
276
  self._value = value
264
- self._type_hint = as_type
265
- if self._type_hint is not None:
266
- self._get_val.__annotations__["return"] = as_type
267
- super().__init__(builder=self._get_val, override=override)
277
+ super().__init__(builder=self._get_val, as_type=as_type, override=override)
268
278
 
269
279
  @property
270
280
  def return_type(self) -> type[T]:
271
- if self._type_hint is not None:
272
- return self._type_hint
281
+ if self._explicit_type is not None:
282
+ return self._explicit_type
273
283
  if isinstance(self._value, list):
274
284
  return list[type(self._value[0])] # type: ignore[misc,return-value]
275
285
  return type(self._value)
@@ -31,7 +31,7 @@ class DependencyGrapher:
31
31
  ) -> list[Node]:
32
32
  nodes: list[Node] = []
33
33
  for root in roots:
34
- for parameter in root.parameter_types:
34
+ for parameter in root.parameter_type_ids:
35
35
  provider = self._providers[parameter]
36
36
 
37
37
  # multiprovider
@@ -75,7 +75,7 @@ class _FastAPIDependencyGrapher(DependencyGrapher):
75
75
  ) -> list[Node]:
76
76
  nodes: list[Node] = []
77
77
  for root in roots:
78
- for parameter in root.parameter_types:
78
+ for parameter in root.parameter_type_ids:
79
79
  provider = self._providers[parameter]
80
80
 
81
81
  # multiprovider
@@ -162,7 +162,7 @@ class APIRouteDependency(Dependency):
162
162
  return self._route
163
163
 
164
164
  @property
165
- def parameter_types(self) -> list[TypeId]:
165
+ def parameter_type_ids(self) -> list[TypeId]:
166
166
  parameters = list(self._signature.parameters.values())
167
167
  if not parameters:
168
168
  return []
@@ -36,4 +36,4 @@ def test_cli_invalid_app_attribute() -> None:
36
36
  def test_cli_invalid_app_instance() -> None:
37
37
  result = runner.invoke(app=cli, args=["tests.cli.test_graph:runner"])
38
38
  assert result.exit_code == 1
39
- assert "engin" in result.output
39
+ assert "Engin" in result.output
@@ -0,0 +1,13 @@
1
+ from typer.testing import CliRunner
2
+
3
+ from engin import Engin
4
+ from engin._cli._inspect import cli
5
+ from tests.deps import ABlock
6
+
7
+ engin = Engin(ABlock)
8
+ runner = CliRunner()
9
+
10
+
11
+ def test_cli_inspect() -> None:
12
+ result = runner.invoke(app=cli, args=["tests.cli.test_inspect:engin", "--type", "float[]"])
13
+ assert result.exit_code == 0
@@ -129,3 +129,15 @@ def test_provides_implicit_overrides_allowed_when_3rd_party():
129
129
 
130
130
  provide_a.apply(engin)
131
131
  provide_b.apply(engin)
132
+
133
+
134
+ def test_provide_as_type():
135
+ provide = Provide(make_int, as_type=float)
136
+ assert provide.return_type is float
137
+ assert provide.signature.return_annotation is float
138
+
139
+
140
+ def test_supply_as_type():
141
+ supply = Supply(3, as_type=float)
142
+ assert supply.return_type is float
143
+ assert supply.signature.return_annotation is float
@@ -206,7 +206,7 @@ toml = [
206
206
 
207
207
  [[package]]
208
208
  name = "engin"
209
- version = "0.0.16"
209
+ version = "0.0.18"
210
210
  source = { editable = "." }
211
211
 
212
212
  [package.optional-dependencies]
@@ -299,36 +299,36 @@ wheels = [
299
299
 
300
300
  [[package]]
301
301
  name = "griffe"
302
- version = "1.7.2"
302
+ version = "1.7.3"
303
303
  source = { registry = "https://pypi.org/simple" }
304
304
  dependencies = [
305
305
  { name = "colorama" },
306
306
  ]
307
- sdist = { url = "https://files.pythonhosted.org/packages/59/08/7df7e90e34d08ad890bd71d7ba19451052f88dc3d2c483d228d1331a4736/griffe-1.7.2.tar.gz", hash = "sha256:98d396d803fab3b680c2608f300872fd57019ed82f0672f5b5323a9ad18c540c", size = 394919 }
307
+ sdist = { url = "https://files.pythonhosted.org/packages/a9/3e/5aa9a61f7c3c47b0b52a1d930302992229d191bf4bc76447b324b731510a/griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b", size = 395137 }
308
308
  wheels = [
309
- { url = "https://files.pythonhosted.org/packages/b1/5e/38b408f41064c9fcdbb0ea27c1bd13a1c8657c4846e04dab9f5ea770602c/griffe-1.7.2-py3-none-any.whl", hash = "sha256:1ed9c2e338a75741fc82083fe5a1bc89cb6142efe126194cc313e34ee6af5423", size = 129187 },
309
+ { url = "https://files.pythonhosted.org/packages/58/c6/5c20af38c2a57c15d87f7f38bee77d63c1d2a3689f74fefaf35915dd12b2/griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75", size = 129303 },
310
310
  ]
311
311
 
312
312
  [[package]]
313
313
  name = "h11"
314
- version = "0.14.0"
314
+ version = "0.16.0"
315
315
  source = { registry = "https://pypi.org/simple" }
316
- sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
316
+ sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 }
317
317
  wheels = [
318
- { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
318
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 },
319
319
  ]
320
320
 
321
321
  [[package]]
322
322
  name = "httpcore"
323
- version = "1.0.8"
323
+ version = "1.0.9"
324
324
  source = { registry = "https://pypi.org/simple" }
325
325
  dependencies = [
326
326
  { name = "certifi" },
327
327
  { name = "h11" },
328
328
  ]
329
- sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385 }
329
+ sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 }
330
330
  wheels = [
331
- { url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732 },
331
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 },
332
332
  ]
333
333
 
334
334
  [[package]]
@@ -527,7 +527,7 @@ wheels = [
527
527
 
528
528
  [[package]]
529
529
  name = "mkdocs-material"
530
- version = "9.6.11"
530
+ version = "9.6.12"
531
531
  source = { registry = "https://pypi.org/simple" }
532
532
  dependencies = [
533
533
  { name = "babel" },
@@ -542,9 +542,9 @@ dependencies = [
542
542
  { name = "pymdown-extensions" },
543
543
  { name = "requests" },
544
544
  ]
545
- sdist = { url = "https://files.pythonhosted.org/packages/5b/7e/c65e330e99daa5813e7594e57a09219ad041ed631604a72588ec7c11b34b/mkdocs_material-9.6.11.tar.gz", hash = "sha256:0b7f4a0145c5074cdd692e4362d232fb25ef5b23328d0ec1ab287af77cc0deff", size = 3951595 }
545
+ sdist = { url = "https://files.pythonhosted.org/packages/2d/ef/25fc10dbbb8faeeeb10ed7734d84a347cd2ec5d7200733f11c5553c02608/mkdocs_material-9.6.12.tar.gz", hash = "sha256:add6a6337b29f9ea7912cb1efc661de2c369060b040eb5119855d794ea85b473", size = 3951532 }
546
546
  wheels = [
547
- { url = "https://files.pythonhosted.org/packages/19/91/79a15a772151aca0d505f901f6bbd4b85ee1fe54100256a6702056bab121/mkdocs_material-9.6.11-py3-none-any.whl", hash = "sha256:47f21ef9cbf4f0ebdce78a2ceecaa5d413581a55141e4464902224ebbc0b1263", size = 8703720 },
547
+ { url = "https://files.pythonhosted.org/packages/09/00/592940f4d150327a4f455171b2c9d4c3be7779a88e18b0a086183fcd8f06/mkdocs_material-9.6.12-py3-none-any.whl", hash = "sha256:92b4fbdc329e4febc267ca6e2c51e8501fa97b2225c5f4deb4d4e43550f8e61e", size = 8703654 },
548
548
  ]
549
549
 
550
550
  [[package]]
@@ -633,20 +633,20 @@ wheels = [
633
633
 
634
634
  [[package]]
635
635
  name = "mypy-extensions"
636
- version = "1.0.0"
636
+ version = "1.1.0"
637
637
  source = { registry = "https://pypi.org/simple" }
638
- sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
638
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 }
639
639
  wheels = [
640
- { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
640
+ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 },
641
641
  ]
642
642
 
643
643
  [[package]]
644
644
  name = "packaging"
645
- version = "24.2"
645
+ version = "25.0"
646
646
  source = { registry = "https://pypi.org/simple" }
647
- sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
647
+ sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 }
648
648
  wheels = [
649
- { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
649
+ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 },
650
650
  ]
651
651
 
652
652
  [[package]]
@@ -696,16 +696,16 @@ wheels = [
696
696
 
697
697
  [[package]]
698
698
  name = "poethepoet"
699
- version = "0.33.1"
699
+ version = "0.34.0"
700
700
  source = { registry = "https://pypi.org/simple" }
701
701
  dependencies = [
702
702
  { name = "pastel" },
703
703
  { name = "pyyaml" },
704
704
  { name = "tomli", marker = "python_full_version < '3.11'" },
705
705
  ]
706
- sdist = { url = "https://files.pythonhosted.org/packages/cd/1d/ec87271390cc5fafdd5996137331ad3a7ce99b715e4ee68db554d202817f/poethepoet-0.33.1.tar.gz", hash = "sha256:8775e09b64f773278b5483659ff238a708723491efadeedd1c2cbf773558cb4c", size = 62536 }
706
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/f2/3853d6a9a0dac08aa680895839eeab8ec0ed63db375e1f782e623c9309b6/poethepoet-0.34.0.tar.gz", hash = "sha256:86203acce555bbfe45cb6ccac61ba8b16a5784264484195874da457ddabf5850", size = 64474 }
707
707
  wheels = [
708
- { url = "https://files.pythonhosted.org/packages/34/ea/c476bfec360eb6831ce46df2719f76d1132b9a87da11c302081a9def5fce/poethepoet-0.33.1-py3-none-any.whl", hash = "sha256:b86d80a81b2ca4e4ce8e8f716cc6004a1a1cdead027778bc07d1c26cb3664770", size = 83512 },
708
+ { url = "https://files.pythonhosted.org/packages/da/d1/61431afe22577083fcb50614bc5e5aa73aa0ab35e3fc2ae49708a59ff70b/poethepoet-0.34.0-py3-none-any.whl", hash = "sha256:c472d6f0fdb341b48d346f4ccd49779840c15b30dfd6bc6347a80d6274b5e34e", size = 85851 },
709
709
  ]
710
710
 
711
711
  [[package]]
@@ -812,15 +812,16 @@ wheels = [
812
812
 
813
813
  [[package]]
814
814
  name = "pydantic-settings"
815
- version = "2.8.1"
815
+ version = "2.9.1"
816
816
  source = { registry = "https://pypi.org/simple" }
817
817
  dependencies = [
818
818
  { name = "pydantic" },
819
819
  { name = "python-dotenv" },
820
+ { name = "typing-inspection" },
820
821
  ]
821
- sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 }
822
+ sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234 }
822
823
  wheels = [
823
- { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 },
824
+ { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 },
824
825
  ]
825
826
 
826
827
  [[package]]
@@ -1007,27 +1008,27 @@ wheels = [
1007
1008
 
1008
1009
  [[package]]
1009
1010
  name = "ruff"
1010
- version = "0.11.5"
1011
- source = { registry = "https://pypi.org/simple" }
1012
- sdist = { url = "https://files.pythonhosted.org/packages/45/71/5759b2a6b2279bb77fe15b1435b89473631c2cd6374d45ccdb6b785810be/ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef", size = 3976488 }
1013
- wheels = [
1014
- { url = "https://files.pythonhosted.org/packages/23/db/6efda6381778eec7f35875b5cbefd194904832a1153d68d36d6b269d81a8/ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b", size = 10103150 },
1015
- { url = "https://files.pythonhosted.org/packages/44/f2/06cd9006077a8db61956768bc200a8e52515bf33a8f9b671ee527bb10d77/ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077", size = 10898637 },
1016
- { url = "https://files.pythonhosted.org/packages/18/f5/af390a013c56022fe6f72b95c86eb7b2585c89cc25d63882d3bfe411ecf1/ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779", size = 10236012 },
1017
- { url = "https://files.pythonhosted.org/packages/b8/ca/b9bf954cfed165e1a0c24b86305d5c8ea75def256707f2448439ac5e0d8b/ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794", size = 10415338 },
1018
- { url = "https://files.pythonhosted.org/packages/d9/4d/2522dde4e790f1b59885283f8786ab0046958dfd39959c81acc75d347467/ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038", size = 9965277 },
1019
- { url = "https://files.pythonhosted.org/packages/e5/7a/749f56f150eef71ce2f626a2f6988446c620af2f9ba2a7804295ca450397/ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f", size = 11541614 },
1020
- { url = "https://files.pythonhosted.org/packages/89/b2/7d9b8435222485b6aac627d9c29793ba89be40b5de11584ca604b829e960/ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82", size = 12198873 },
1021
- { url = "https://files.pythonhosted.org/packages/00/e0/a1a69ef5ffb5c5f9c31554b27e030a9c468fc6f57055886d27d316dfbabd/ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304", size = 11670190 },
1022
- { url = "https://files.pythonhosted.org/packages/05/61/c1c16df6e92975072c07f8b20dad35cd858e8462b8865bc856fe5d6ccb63/ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470", size = 13902301 },
1023
- { url = "https://files.pythonhosted.org/packages/79/89/0af10c8af4363304fd8cb833bd407a2850c760b71edf742c18d5a87bb3ad/ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a", size = 11350132 },
1024
- { url = "https://files.pythonhosted.org/packages/b9/e1/ecb4c687cbf15164dd00e38cf62cbab238cad05dd8b6b0fc68b0c2785e15/ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b", size = 10312937 },
1025
- { url = "https://files.pythonhosted.org/packages/cf/4f/0e53fe5e500b65934500949361e3cd290c5ba60f0324ed59d15f46479c06/ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a", size = 9936683 },
1026
- { url = "https://files.pythonhosted.org/packages/04/a8/8183c4da6d35794ae7f76f96261ef5960853cd3f899c2671961f97a27d8e/ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159", size = 10950217 },
1027
- { url = "https://files.pythonhosted.org/packages/26/88/9b85a5a8af21e46a0639b107fcf9bfc31da4f1d263f2fc7fbe7199b47f0a/ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783", size = 11404521 },
1028
- { url = "https://files.pythonhosted.org/packages/fc/52/047f35d3b20fd1ae9ccfe28791ef0f3ca0ef0b3e6c1a58badd97d450131b/ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe", size = 10320697 },
1029
- { url = "https://files.pythonhosted.org/packages/b9/fe/00c78010e3332a6e92762424cf4c1919065707e962232797d0b57fd8267e/ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800", size = 11378665 },
1030
- { url = "https://files.pythonhosted.org/packages/43/7c/c83fe5cbb70ff017612ff36654edfebec4b1ef79b558b8e5fd933bab836b/ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e", size = 10460287 },
1011
+ version = "0.11.7"
1012
+ source = { registry = "https://pypi.org/simple" }
1013
+ sdist = { url = "https://files.pythonhosted.org/packages/5b/89/6f9c9674818ac2e9cc2f2b35b704b7768656e6b7c139064fc7ba8fbc99f1/ruff-0.11.7.tar.gz", hash = "sha256:655089ad3224070736dc32844fde783454f8558e71f501cb207485fe4eee23d4", size = 4054861 }
1014
+ wheels = [
1015
+ { url = "https://files.pythonhosted.org/packages/b4/ec/21927cb906c5614b786d1621dba405e3d44f6e473872e6df5d1a6bca0455/ruff-0.11.7-py3-none-linux_armv6l.whl", hash = "sha256:d29e909d9a8d02f928d72ab7837b5cbc450a5bdf578ab9ebee3263d0a525091c", size = 10245403 },
1016
+ { url = "https://files.pythonhosted.org/packages/e2/af/fec85b6c2c725bcb062a354dd7cbc1eed53c33ff3aa665165871c9c16ddf/ruff-0.11.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dd1fb86b168ae349fb01dd497d83537b2c5541fe0626e70c786427dd8363aaee", size = 11007166 },
1017
+ { url = "https://files.pythonhosted.org/packages/31/9a/2d0d260a58e81f388800343a45898fd8df73c608b8261c370058b675319a/ruff-0.11.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d3d7d2e140a6fbbc09033bce65bd7ea29d6a0adeb90b8430262fbacd58c38ada", size = 10378076 },
1018
+ { url = "https://files.pythonhosted.org/packages/c2/c4/9b09b45051404d2e7dd6d9dbcbabaa5ab0093f9febcae664876a77b9ad53/ruff-0.11.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4809df77de390a1c2077d9b7945d82f44b95d19ceccf0c287c56e4dc9b91ca64", size = 10557138 },
1019
+ { url = "https://files.pythonhosted.org/packages/5e/5e/f62a1b6669870a591ed7db771c332fabb30f83c967f376b05e7c91bccd14/ruff-0.11.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3a0c2e169e6b545f8e2dba185eabbd9db4f08880032e75aa0e285a6d3f48201", size = 10095726 },
1020
+ { url = "https://files.pythonhosted.org/packages/45/59/a7aa8e716f4cbe07c3500a391e58c52caf665bb242bf8be42c62adef649c/ruff-0.11.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49b888200a320dd96a68e86736cf531d6afba03e4f6cf098401406a257fcf3d6", size = 11672265 },
1021
+ { url = "https://files.pythonhosted.org/packages/dd/e3/101a8b707481f37aca5f0fcc3e42932fa38b51add87bfbd8e41ab14adb24/ruff-0.11.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2b19cdb9cf7dae00d5ee2e7c013540cdc3b31c4f281f1dacb5a799d610e90db4", size = 12331418 },
1022
+ { url = "https://files.pythonhosted.org/packages/dd/71/037f76cbe712f5cbc7b852e4916cd3cf32301a30351818d32ab71580d1c0/ruff-0.11.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64e0ee994c9e326b43539d133a36a455dbaab477bc84fe7bfbd528abe2f05c1e", size = 11794506 },
1023
+ { url = "https://files.pythonhosted.org/packages/ca/de/e450b6bab1fc60ef263ef8fcda077fb4977601184877dce1c59109356084/ruff-0.11.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bad82052311479a5865f52c76ecee5d468a58ba44fb23ee15079f17dd4c8fd63", size = 13939084 },
1024
+ { url = "https://files.pythonhosted.org/packages/0e/2c/1e364cc92970075d7d04c69c928430b23e43a433f044474f57e425cbed37/ruff-0.11.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7940665e74e7b65d427b82bffc1e46710ec7f30d58b4b2d5016e3f0321436502", size = 11450441 },
1025
+ { url = "https://files.pythonhosted.org/packages/9d/7d/1b048eb460517ff9accd78bca0fa6ae61df2b276010538e586f834f5e402/ruff-0.11.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:169027e31c52c0e36c44ae9a9c7db35e505fee0b39f8d9fca7274a6305295a92", size = 10441060 },
1026
+ { url = "https://files.pythonhosted.org/packages/3a/57/8dc6ccfd8380e5ca3d13ff7591e8ba46a3b330323515a4996b991b10bd5d/ruff-0.11.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:305b93f9798aee582e91e34437810439acb28b5fc1fee6b8205c78c806845a94", size = 10058689 },
1027
+ { url = "https://files.pythonhosted.org/packages/23/bf/20487561ed72654147817885559ba2aa705272d8b5dee7654d3ef2dbf912/ruff-0.11.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a681db041ef55550c371f9cd52a3cf17a0da4c75d6bd691092dfc38170ebc4b6", size = 11073703 },
1028
+ { url = "https://files.pythonhosted.org/packages/9d/27/04f2db95f4ef73dccedd0c21daf9991cc3b7f29901a4362057b132075aa4/ruff-0.11.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:07f1496ad00a4a139f4de220b0c97da6d4c85e0e4aa9b2624167b7d4d44fd6b6", size = 11532822 },
1029
+ { url = "https://files.pythonhosted.org/packages/e1/72/43b123e4db52144c8add336581de52185097545981ff6e9e58a21861c250/ruff-0.11.7-py3-none-win32.whl", hash = "sha256:f25dfb853ad217e6e5f1924ae8a5b3f6709051a13e9dad18690de6c8ff299e26", size = 10362436 },
1030
+ { url = "https://files.pythonhosted.org/packages/c5/a0/3e58cd76fdee53d5c8ce7a56d84540833f924ccdf2c7d657cb009e604d82/ruff-0.11.7-py3-none-win_amd64.whl", hash = "sha256:0a931d85959ceb77e92aea4bbedfded0a31534ce191252721128f77e5ae1f98a", size = 11566676 },
1031
+ { url = "https://files.pythonhosted.org/packages/68/ca/69d7c7752bce162d1516e5592b1cc6b6668e9328c0d270609ddbeeadd7cf/ruff-0.11.7-py3-none-win_arm64.whl", hash = "sha256:778c1e5d6f9e91034142dfd06110534ca13220bfaad5c3735f6cb844654f6177", size = 10677936 },
1031
1032
  ]
1032
1033
 
1033
1034
  [[package]]
@@ -1155,16 +1156,16 @@ wheels = [
1155
1156
 
1156
1157
  [[package]]
1157
1158
  name = "uvicorn"
1158
- version = "0.34.1"
1159
+ version = "0.34.2"
1159
1160
  source = { registry = "https://pypi.org/simple" }
1160
1161
  dependencies = [
1161
1162
  { name = "click" },
1162
1163
  { name = "h11" },
1163
1164
  { name = "typing-extensions", marker = "python_full_version < '3.11'" },
1164
1165
  ]
1165
- sdist = { url = "https://files.pythonhosted.org/packages/86/37/dd92f1f9cedb5eaf74d9999044306e06abe65344ff197864175dbbd91871/uvicorn-0.34.1.tar.gz", hash = "sha256:af981725fc4b7ffc5cb3b0e9eda6258a90c4b52cb2a83ce567ae0a7ae1757afc", size = 76755 }
1166
+ sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815 }
1166
1167
  wheels = [
1167
- { url = "https://files.pythonhosted.org/packages/5f/38/a5801450940a858c102a7ad9e6150146a25406a119851c993148d56ab041/uvicorn-0.34.1-py3-none-any.whl", hash = "sha256:984c3a8c7ca18ebaad15995ee7401179212c59521e67bfc390c07fa2b8d2e065", size = 62404 },
1168
+ { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483 },
1168
1169
  ]
1169
1170
 
1170
1171
  [[package]]
@@ -1,18 +0,0 @@
1
- from typing import Never
2
-
3
- import typer
4
- from rich import print
5
- from rich.panel import Panel
6
-
7
-
8
- def print_error(msg: str) -> Never:
9
- print(
10
- Panel(
11
- title="Error",
12
- renderable=msg.capitalize(),
13
- title_align="left",
14
- border_style="red",
15
- highlight=True,
16
- )
17
- )
18
- raise typer.Exit(code=1)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes