arcade-core 2.0.0__py3-none-any.whl → 2.1.0__py3-none-any.whl

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.
arcade_core/toolkit.py CHANGED
@@ -38,6 +38,13 @@ class Toolkit(BaseModel):
38
38
  """
39
39
  Validator to strip the 'arcade_' prefix from the name if it exists.
40
40
  """
41
+ return cls._strip_arcade_prefix(value)
42
+
43
+ @classmethod
44
+ def _strip_arcade_prefix(cls, value: str) -> str:
45
+ """
46
+ Strip the 'arcade_' prefix from the name if it exists.
47
+ """
41
48
  if value.startswith("arcade_"):
42
49
  return value[len("arcade_") :]
43
50
  return value
@@ -68,24 +75,24 @@ class Toolkit(BaseModel):
68
75
  repo = metadata.get("Repository", None) # type: ignore[attr-defined]
69
76
 
70
77
  except importlib.metadata.PackageNotFoundError as e:
71
- raise ToolkitLoadError(f"Package {package} not found.") from e
78
+ raise ToolkitLoadError(f"Package '{package}' not found.") from e
72
79
  except KeyError as e:
73
- raise ToolkitLoadError(f"Metadata key error for package {package}.") from e
80
+ raise ToolkitLoadError(f"Metadata key error for package '{package}'.") from e
74
81
  except Exception as e:
75
- raise ToolkitLoadError(f"Failed to load metadata for package {package}.") from e
82
+ raise ToolkitLoadError(f"Failed to load metadata for package '{package}'.") from e
76
83
 
77
84
  # Get the package directory
78
85
  try:
79
86
  package_dir = Path(get_package_directory(package))
80
87
  except (ImportError, AttributeError) as e:
81
- raise ToolkitLoadError(f"Failed to locate package directory for {package}.") from e
88
+ raise ToolkitLoadError(f"Failed to locate package directory for '{package}'.") from e
82
89
 
83
90
  # Get all python files in the package directory
84
91
  try:
85
92
  modules = [f for f in package_dir.glob("**/*.py") if f.is_file()]
86
93
  except OSError as e:
87
94
  raise ToolkitLoadError(
88
- f"Failed to locate Python files in package directory for {package}."
95
+ f"Failed to locate Python files in package directory for '{package}'."
89
96
  ) from e
90
97
 
91
98
  toolkit = cls(
@@ -110,31 +117,118 @@ class Toolkit(BaseModel):
110
117
  return toolkit
111
118
 
112
119
  @classmethod
113
- def find_all_arcade_toolkits(cls) -> list["Toolkit"]:
120
+ def from_entrypoint(cls, entry: importlib.metadata.EntryPoint) -> "Toolkit":
114
121
  """
115
- Find all installed packages prefixed with 'arcade_' in the current
116
- Python interpreter's environment and load them as Toolkits.
122
+ Load a Toolkit from an entrypoint.
123
+
124
+ The entrypoint value is used as the toolkit name, while the package name
125
+ is extracted from the distribution that owns the entrypoint.
126
+
127
+ Args:
128
+ entry: The EntryPoint object from importlib.metadata
117
129
 
118
130
  Returns:
119
- List[Toolkit]: A list of Toolkit instances.
131
+ A Toolkit instance
132
+
133
+ Raises:
134
+ ToolkitLoadError: If the toolkit cannot be loaded
135
+ """
136
+ # Get the package name from the distribution that owns this entrypoint
137
+ if not hasattr(entry, "dist") or entry.dist is None:
138
+ raise ToolkitLoadError(
139
+ f"Entry point '{entry.name}' does not have distribution metadata. "
140
+ f"This may indicate an incomplete package installation."
141
+ )
142
+
143
+ package_name = entry.dist.name
144
+
145
+ toolkit = cls.from_package(package_name)
146
+ toolkit.name = cls._strip_arcade_prefix(entry.value)
147
+
148
+ return toolkit
149
+
150
+ @classmethod
151
+ def find_arcade_toolkits_from_entrypoints(cls) -> list["Toolkit"]:
152
+ """
153
+ Find and load as Toolkits all installed packages in the
154
+ current Python interpreter's environment that have a
155
+ registered entrypoint under the 'arcade.toolkits' group.
156
+ """
157
+ toolkits = []
158
+ toolkit_entries: list[importlib.metadata.EntryPoint] = []
159
+
160
+ try:
161
+ toolkit_entries = importlib.metadata.entry_points(
162
+ group="arcade_toolkits", name="toolkit_name"
163
+ )
164
+ for entry in toolkit_entries:
165
+ try:
166
+ toolkit = cls.from_entrypoint(entry)
167
+ toolkits.append(toolkit)
168
+ logger.debug(
169
+ f"Loaded toolkit from entry point: {entry.name} = '{toolkit.name}'"
170
+ )
171
+ except ToolkitLoadError as e:
172
+ logger.warning(
173
+ f"Warning: {e} Skipping toolkit from entry point '{entry.value}'"
174
+ )
175
+ except Exception as e:
176
+ logger.debug(f"Entry point discovery failed or not available: {e}")
177
+
178
+ return toolkits
179
+
180
+ @classmethod
181
+ def find_arcade_toolkits_from_prefix(cls) -> list["Toolkit"]:
182
+ """
183
+ Find and load as Toolkits all installed packages in the
184
+ current Python interpreter's environment that are prefixed with 'arcade_'.
120
185
  """
121
186
  import sysconfig
122
187
 
123
- # Get the site-packages directory of the current interpreter
188
+ toolkits = []
124
189
  site_packages_dir = sysconfig.get_paths()["purelib"]
190
+
125
191
  arcade_packages = [
126
192
  dist.metadata["Name"]
127
193
  for dist in importlib.metadata.distributions(path=[site_packages_dir])
128
194
  if dist.metadata["Name"].startswith("arcade_")
129
195
  ]
130
- toolkits = []
196
+
131
197
  for package in arcade_packages:
132
198
  try:
133
- toolkits.append(cls.from_package(package))
199
+ toolkit = cls.from_package(package)
200
+ toolkits.append(toolkit)
201
+ logger.debug(f"Loaded toolkit from prefix discovery: {package}")
134
202
  except ToolkitLoadError as e:
135
203
  logger.warning(f"Warning: {e} Skipping toolkit {package}")
204
+
136
205
  return toolkits
137
206
 
207
+ @classmethod
208
+ def find_all_arcade_toolkits(cls) -> list["Toolkit"]:
209
+ """
210
+ Find and load as Toolkits all installed packages in the
211
+ current Python interpreter's environment that either
212
+ 1. Have a registered entrypoint under the 'arcade.toolkits' group, or
213
+ 2. Are prefixed with 'arcade_'
214
+
215
+ Returns:
216
+ List[Toolkit]: A list of Toolkit instances.
217
+ """
218
+ # Find toolkits
219
+ entrypoint_toolkits = cls.find_arcade_toolkits_from_entrypoints()
220
+ prefix_toolkits = cls.find_arcade_toolkits_from_prefix()
221
+
222
+ # Deduplicate. Entrypoints are preferred over prefix-based toolkits.
223
+ seen_package_names = set()
224
+ all_toolkits = []
225
+ for toolkit in entrypoint_toolkits + prefix_toolkits:
226
+ if toolkit.package_name not in seen_package_names:
227
+ all_toolkits.append(toolkit)
228
+ seen_package_names.add(toolkit.package_name)
229
+
230
+ return all_toolkits
231
+
138
232
 
139
233
  def get_package_directory(package_name: str) -> str:
140
234
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arcade-core
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Arcade Core - Core library for Arcade platform
5
5
  Author-email: Arcade <dev@arcade.dev>
6
6
  License: MIT
@@ -59,17 +59,21 @@ pip install arcade-core
59
59
 
60
60
  ## Usage
61
61
 
62
+ 1. Install an arcade toolkit
63
+ ```bash
64
+ pip install arcade-math
65
+ ```
66
+
67
+ 2. Load the toolkit
62
68
  ```python
63
- from arcade_core import ToolCatalog, Toolkit, ArcadeConfig
69
+ import arcade_math
70
+ from arcade_core import ToolCatalog, Toolkit
64
71
 
65
72
  # Create a tool catalog
66
73
  catalog = ToolCatalog()
67
74
 
68
75
  # Load a toolkit
69
- toolkit = Toolkit.from_directory("path/to/toolkit")
70
-
71
- # Configure Arcade
72
- config = ArcadeConfig.from_file("config.yaml")
76
+ toolkit = Toolkit.from_module(arcade_math)
73
77
  ```
74
78
 
75
79
  ## License
@@ -11,9 +11,9 @@ arcade_core/parse.py,sha256=SURNI-B9xHCIprxTRTAR0AMT9hIJpQqHjOmrENzFBVI,1899
11
11
  arcade_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  arcade_core/schema.py,sha256=DgVqrRDZMhndhXcb-CvLEnAtVWkseisvtAwcei5Qgmc,14358
13
13
  arcade_core/telemetry.py,sha256=qDv8T-wO8nFi0Qh93WKaPH1b6asfoJoyyfA7ZOxPnbA,5566
14
- arcade_core/toolkit.py,sha256=ufxkyRN2Qmu-TW4GHYuXrsMav3lrTusUqYf_wL-WZdw,5349
14
+ arcade_core/toolkit.py,sha256=AdDaUpNvKst1IBnaLegfQpRg9dHxmK-9cOSQqzBpS4I,8763
15
15
  arcade_core/utils.py,sha256=Gg4na-85pY21e5Ab-yxoRlzTQu3FhlP5xQ9G1BhfrI8,2980
16
16
  arcade_core/version.py,sha256=CpXi3jGlx23RvRyU7iytOMZrnspdWw4yofS8lpP1AJU,18
17
- arcade_core-2.0.0.dist-info/METADATA,sha256=g093vgoWqzSNgBs0uExT7dlassVKXEPXc39-9zB9PhA,2542
18
- arcade_core-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- arcade_core-2.0.0.dist-info/RECORD,,
17
+ arcade_core-2.1.0.dist-info/METADATA,sha256=PIt7VzQlG424AQVMrYLnq-ZZeDTXTCGNbV4x-16xols,2557
18
+ arcade_core-2.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ arcade_core-2.1.0.dist-info/RECORD,,