Skip to content

Commit ed0add6

Browse files
committed
update docs
1 parent b53e795 commit ed0add6

File tree

3 files changed

+59
-7
lines changed

3 files changed

+59
-7
lines changed

src/scyjava/_stubs/_cli.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
"""The scyjava-stubs executable."""
1+
"""The scyjava-stubs executable.
2+
3+
Provides cli access to the `scyjava._stubs.generate_stubs` function.
4+
5+
The only interesting additional things going on here is the choice of *where* the stubs
6+
go by default. When using the CLI, they land in `scyjava.types` by default; see the
7+
`_get_ouput_dir` helper function for details on how the output directory is resolved
8+
from the CLI arguments.
9+
"""
210

311
from __future__ import annotations
412

src/scyjava/_stubs/_dynamic_import.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
"""Logic for using generated type stubs as runtime importable, with lazy JVM startup.
2+
3+
Most often, the functionality here will be used as follows:
4+
5+
```
6+
from scyjava._stubs import setup_java_imports
7+
8+
__all__, __getattr__ = setup_java_imports(
9+
__name__,
10+
__file__,
11+
endpoints=["org.scijava:parsington:3.1.0"],
12+
base_prefix="org"
13+
)
14+
```
15+
16+
...and that little snippet is written into the generated stubs modules by the
17+
`scyjava._stubs.generate_stubs` function.
18+
19+
See docstring of `setup_java_imports` for details on how it works.
20+
"""
21+
122
import ast
223
from logging import warning
324
from pathlib import Path
@@ -21,11 +42,14 @@ def setup_java_imports(
2142
:param module_file: The path to the module file (usually `__file__` in the calling
2243
module).
2344
:param endpoints: A list of Java endpoints to add to the scyjava configuration.
45+
(Note that `scyjava._stubs.generate_stubs` will automatically add the necessary
46+
endpoints for the generated stubs.)
2447
:param base_prefix: The base prefix for the Java package name. This is used when
2548
determining the Java class path for the requested class. The java class path
2649
will be truncated to only the part including the base_prefix and after. This
2750
makes it possible to embed a module in a subpackage (like `scyjava.types`) and
2851
still have the correct Java class path.
52+
2953
:return: A 2-tuple containing:
3054
- A list of all classes in the module (as defined in the stub file), to be
3155
assigned to `__all__`.
@@ -57,6 +81,7 @@ def setup_java_imports(
5781
if ep not in scyjava.config.endpoints:
5882
scyjava.config.endpoints.append(ep)
5983

84+
# list intended to be assigned to `__all__` in the generated module.
6085
module_all = []
6186
try:
6287
my_stub = Path(module_file).with_suffix(".pyi")
@@ -75,6 +100,7 @@ def setup_java_imports(
75100
)
76101

77102
def module_getattr(name: str, mod_name: str = module_name) -> Any:
103+
"""Function intended to be assigned to __getattr__ in the generate module."""
78104
if module_all and name not in module_all:
79105
raise AttributeError(f"module {module_name!r} has no attribute {name!r}")
80106

@@ -84,6 +110,9 @@ def module_getattr(name: str, mod_name: str = module_name) -> Any:
84110

85111
class_path = f"{mod_name}.{name}"
86112

113+
# Generate a proxy type (with a nice repr) that
114+
# delays the call to `jimport` until the last moment when type.__new__ is called
115+
87116
class ProxyMeta(type):
88117
def __repr__(self) -> str:
89118
return f"<scyjava class {class_path!r}>"

src/scyjava/_stubs/_genstubs.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
"""Type stub generation utilities using stubgen.
2+
3+
This module provides utilities for generating type stubs for Java classes
4+
using the stubgenj library. `stubgenj` must be installed for this to work
5+
(it, in turn, only depends on JPype).
6+
7+
See `generate_stubs` for most functionality. For the command-line tool,
8+
see `scyjava._stubs.cli`, which provides a CLI interface for the `generate_stubs`
9+
function.
10+
"""
11+
112
from __future__ import annotations
213

314
import ast
@@ -42,9 +53,11 @@ def generate_stubs(
4253
The prefixes to generate stubs for. This should be a list of Java class
4354
prefixes that you expect to find in the endpoints. For example,
4455
["org.apache.commons"]. If not provided, the prefixes will be
45-
automatically determined from the jar files provided by endpoints.
56+
automatically determined from the jar files provided by endpoints (see the
57+
`_list_top_level_packages` helper function).
4658
output_dir : str | Path, optional
47-
The directory to write the generated stubs to. Defaults to "stubs".
59+
The directory to write the generated stubs to. Defaults to "stubs" in the
60+
current working directory.
4861
convert_strings : bool, optional
4962
Whether to cast Java strings to Python strings in the stubs. Defaults to True.
5063
NOTE: This leads to type stubs that may not be strictly accurate at runtime.
@@ -58,8 +71,10 @@ def generate_stubs(
5871
Whether to include Javadoc in the generated stubs. Defaults to True.
5972
add_runtime_imports : bool, optional
6073
Whether to add runtime imports to the generated stubs. Defaults to True.
61-
This is useful if you want to use the stubs as a runtime package with type
62-
safety.
74+
This is useful if you want to actually import the stubs as a runtime package
75+
with type safety. The runtime import "magic" depends on the
76+
`scyjava._stubs.setup_java_imports` function. See its documentation for
77+
more details.
6378
remove_namespace_only_stubs : bool, optional
6479
Whether to remove stubs that export no names beyond a single
6580
`__module_protocol__`. This leaves some folders as PEP420 implicit namespace
@@ -95,7 +110,7 @@ def _patched_start(*args: Any, **kwargs: Any) -> None:
95110
ep_artifacts = tuple(ep.split(":")[1] for ep in endpoints)
96111
for j in cp.split(os.pathsep):
97112
if Path(j).name.startswith(ep_artifacts):
98-
_prefixes.update(list_top_level_packages(j))
113+
_prefixes.update(_list_top_level_packages(j))
99114

100115
prefixes = sorted(_prefixes)
101116
logger.info(f"Using endpoints: {scyjava.config.endpoints!r}")
@@ -189,7 +204,7 @@ def ruff_check(output: Path, select: str = "E,W,F,I,UP,C4,B,RUF,TC,TID") -> None
189204
subprocess.run(["ruff", "format", *py_files, "--quiet"])
190205

191206

192-
def list_top_level_packages(jar_path: str) -> set[str]:
207+
def _list_top_level_packages(jar_path: str) -> set[str]:
193208
"""Inspect a JAR file and return the set of top-level Java package names."""
194209
packages: set[str] = set()
195210
with ZipFile(jar_path, "r") as jar:

0 commit comments

Comments
 (0)