Skip to content

Commit

Permalink
feat: remove dynamic imports
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBRas committed Feb 12, 2024
1 parent fdf5aa4 commit 211e9de
Show file tree
Hide file tree
Showing 12 changed files with 37 additions and 129 deletions.
86 changes: 1 addition & 85 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ It's meant to solve two issues with using polars API extensions:
Polugins exposes some standard ways to use API extensions - both for your own and third party packages -
and then use this discoverability to also generate type stubs with the added typing from the extensions.

Users can either call `register_namespaces` themselves or import polars through `polugins.polars` instead.
Users can either call `register_namespaces` themselves or import polars through `polugins.polars`.
Lint rules can then be used to enforce that nothing is imported from polars outside of these locations.

Types are generated with the package `polugins_type_gen`. This creates static type stubs with the extensions added to them.
Expand Down Expand Up @@ -96,12 +96,6 @@ Namespaces can be registered in three ways:
- From entry points
- From environment variables

They can also be imported:
- By module path
- As imported module

but then types cannot be generated for them so the three first methods are recommended.

### Register from pyproject.toml or polugins.toml
In either your `pyproject.toml` or a file called `polugins.toml` put

Expand Down Expand Up @@ -137,55 +131,6 @@ polugins_lazyframe_my_namespace=my_package.namespaces:MyNamespace

Which will register a `LazyFrame` namespace located at `my_package.namespaces` called `MyNamespace` as `my_namespace`.

### From module path
In the `register_namespaces` call:
```python
register_namespaces(
<extension_class>_namespaces={
'<namespace_name>': "<path>:<class_name>"
},
)
```
Concrete example:
```python
register_namespaces(
lazyframe_namespaces={
'my_namespace': "my_package:MyNamespace"
},
)
```

Which will register a `LazyFrame` namespace located at `my_package.namespaces` called `MyNamespace` as `my_namespace`.

Note that types cannot be generated for namespaces registered with this method

### As imported module
In the `register_namespaces` call:
```python
from <path> import <class_name>

register_namespaces(
<extension_class>_namespaces={
'<namespace_name>': <class_name>
},
)
```
Concrete example:
```python
from my_package import MyNamespace

register_namespaces(
lazyframe_namespaces={
'my_namespace': MyNamespace
},
)
```

Which will register a `LazyFrame` namespace located at `my_package.namespaces` called `MyNamespace` as `my_namespace`.

Note that types cannot be generated for namespaces registered with this method


## Generate types

To generate types install the python package `polugins_type_gen` and then run `polugins stubs` to create type stubs at "./typings".
Expand Down Expand Up @@ -214,35 +159,6 @@ If using another tool than `poetry`, use their equivalent way of exposing endpoi

Don't use the `pl.api.register_x` in your package. This will make the extension be registered on import which we specifically want to avoid.

## Kitchen sink


```python
from polugins import register_namespaces
import polars as pl
from my_package import MyNamespace

register_namespaces(
# NOTE: Types can be generated for types registered like this:
load_entrypoints=True # Loads from example-package
load_config=True # Loads from pyproject.toml and polugins.toml
load_env=True # Loads from environment variables
# NOTE: Namespaces can also be registered like below but types are NOT be generated for them
lazyframe_namespaces={
'my_namespace': MyNamespace,
'also_my_namespace': "my_package:AlsoMyNamespace" # Note the `:` to separate module path from object
},
)

# All namespaces are now registered
(
pl.LazyFrame()
.external.some_method()
.my_namespace.some_method()
.also_my_namespace.some_method()
)
```


## Implementation

Expand Down
4 changes: 4 additions & 0 deletions polugins/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Unreleased

# 0.5.0
- Remove ability to load extensions inline with module path or module import.
2 changes: 1 addition & 1 deletion polugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

Register API extensions for Polars.

See full information at [Github](https://github.com/StefanBRas/polugins)
See full information at [Github](https://github.com/StefanBRas/polugins).
2 changes: 1 addition & 1 deletion polugins/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "polugins"
version = "0.4.0"
version = "0.5.0"
description = "Plugin system for Polars."
authors = ["StefanBRas <opensource@bruhn.io>"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion polugins/src/polugins/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.0"
__version__ = "0.5.0"
16 changes: 2 additions & 14 deletions polugins/src/polugins/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,11 @@ def _get_namespaces(

def register_namespaces(
*,
lazyframe_namespaces: NamespaceDict = {},
dataframe_namespaces: NamespaceDict = {},
expression_namespaces: NamespaceDict = {},
series_namespaces: NamespaceDict = {},
load_entrypoints: bool = True,
load_config: bool = True,
load_env: bool = True,
):
for name, namespace in lazyframe_namespaces.items():
ExtensionClass.LAZYFRAME.register(name, namespace)
for name, namespace in dataframe_namespaces.items():
ExtensionClass.DATAFRAME.register(name, namespace)
for name, namespace in expression_namespaces.items():
ExtensionClass.EXPR.register(name, namespace)
for name, namespace in series_namespaces.items():
ExtensionClass.SERIES.register(name, namespace)
other_namespaces = _get_namespaces(load_entrypoints, load_config, load_env)
for extension_class, namespaces in other_namespaces.items():
namespaces = _get_namespaces(load_entrypoints, load_config, load_env)
for extension_class, namespaces in namespaces.items():
for name, namespace in namespaces.items():
extension_class.register(name, namespace)
17 changes: 0 additions & 17 deletions polugins/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,6 @@
from polugins.main import register_namespaces


def test_custom():
ldf = LazyFrame()
from polugins._testing.namespaces import CustomNamespace

register_namespaces(
lazyframe_namespaces={
"custom": CustomNamespace,
"custom_path": "polugins._testing.namespaces:CustomNamespaceByPath",
},
load_entrypoints=False,
load_config=False,
load_env=False,
)
ldf.custom.custom_method(x=1)
ldf.custom_path.custom_method(x=1)


def test_external():
ldf = LazyFrame()
register_namespaces(load_entrypoints=True, load_config=True, load_env=True)
Expand Down
5 changes: 5 additions & 0 deletions polugins_type_gen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

# Unreleased

# 0.5.0
- Fixed all stubs, they would accidentally be all the same before.
4 changes: 1 addition & 3 deletions polugins_type_gen/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "polugins_type_gen"
version = "0.4.0"
version = "0.5.0"
description = "Type stub generator Polugins."
authors = ["StefanBRas <opensource@bruhn.io>"]
readme = "README.md"
Expand All @@ -14,9 +14,7 @@ polugins = ">=0.2"
optional = true

[tool.poetry.group.dev.dependencies]
polugins = ">=0.2" # We use it for the scripts/generate_polars_stubs.py
pytest = "^7.3.1"
mypy = "1.7.1" # Not sure it's considered Public API what we import, so we fix the version.
ruff = "0.1.11"
nox = "^2023.4.22"
coverage = "^7.2.7"
Expand Down
7 changes: 5 additions & 2 deletions polugins_type_gen/scripts/generate_polars_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,14 @@ def comparison_section(
body += f"## {extension_class}\n{diff_chunk(diff)}"
return header + body


def newest_version_not_in(versions: set[version.Version]) -> version.Version:
current_versions = get_current_versions()
return max(current_versions - versions)


def create_pr_body(versions: set[version.Version], tempdir_path: Path):
newest_current_version = newest_version_not_in(versions)
newest_current_version = newest_version_not_in(versions)

comparisons = {
(version_1, version_2): compare_versions(version_1, version_2, tempdir_path)
Expand All @@ -159,7 +160,9 @@ def create_pr_body(versions: set[version.Version], tempdir_path: Path):
return header + body


def compare_versions(version_1: version.Version, version_2, tempdir_path: Path) -> list[tuple[str, str]]:
def compare_versions(
version_1: version.Version, version_2, tempdir_path: Path
) -> list[tuple[str, str]]:
results = []
stub_dir_1 = tempdir_path / "no_docstring" / str(version_1)
stub_dir_2 = tempdir_path / "no_docstring" / str(version_2)
Expand Down
6 changes: 1 addition & 5 deletions polugins_type_gen/src/polugins_type_gen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,7 @@ def create_stubs(version: str):
all_namespaces = _get_namespaces()

if all(namespace == {} for namespace in all_namespaces.values()):
msg = (
"No namespaces found. No types will be generated as this is usually an error."
" Note that only namespaces registered through config, env vars or endpoints"
" can have types genered for them."
)
msg = "No namespaces found. No types will be generated as this is usually an error."
raise NoNamespaceRegisteredException(msg)

for extension_class, namespaces in all_namespaces.items():
Expand Down
15 changes: 15 additions & 0 deletions polugins_type_gen/tests/test_stubs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from pathlib import Path

import pytest

stubs_dir = Path(__file__).parent.parent / Path("src", "polugins_type_gen", "_stubs")
all_stub_files = sorted(p for p in stubs_dir.rglob("*") if p.is_file())


def get_id(p: Path):
return f"{p.parts[-4]}_{p.parts[-1]}"


@pytest.mark.parametrize("stub_path", all_stub_files, ids=get_id)
def test_stubs(stub_path: Path):
assert "Incomplete" not in stub_path.read_text()

0 comments on commit 211e9de

Please sign in to comment.