Skip to content

Commit

Permalink
chore: Add missing typing information to meltano.core.project (#8925)
Browse files Browse the repository at this point in the history
* chore: Add missing typing information to `meltano.core.project`

* Add test for `meltano --environment ... select`

* Refactor to get rid of typing errors
  • Loading branch information
edgarrmondragon authored Nov 22, 2024
1 parent 44ea54d commit 163da2a
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 38 deletions.
3 changes: 3 additions & 0 deletions src/meltano/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
if t.TYPE_CHECKING:
from meltano.core.project import Project
from meltano.core.project_settings_service import ProjectSettingsService
from meltano.core.settings_service import SettingsService

logger = structlog.stdlib.get_logger(__name__)

Expand Down Expand Up @@ -187,6 +188,8 @@ def config(

_, Session = project_engine(project) # noqa: N806
session = Session()

settings: SettingsService
try:
if plugin:
settings = PluginSettingsService(project, plugin)
Expand Down
4 changes: 2 additions & 2 deletions src/meltano/core/manifest/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

from meltano import schemas
from meltano.core.manifest.jsonschema import meltano_config_env_locations
from meltano.core.plugin.base import PluginType
from meltano.core.plugin.settings_service import PluginSettingsService
from meltano.core.plugin_lock_service import PluginLock
from meltano.core.utils import (
Expand All @@ -36,7 +37,6 @@
from collections.abc import Iterable, MutableMapping
from pathlib import Path

from meltano.core.plugin.base import PluginType
from meltano.core.plugin.project_plugin import ProjectPlugin
from meltano.core.project import Project

Expand Down Expand Up @@ -386,7 +386,7 @@ def data(self) -> dict[str, t.Any]:
# `meltano.yml` or in manifest files. For more details, refer to:
# https://gitlab.com/meltano/meltano/-/merge_requests/2481#note_832478775
with suppress(KeyError):
del plugins["mappings"]
del plugins[PluginType.MAPPINGS]

# NOTE: `self._merge_plugin_lockfiles` restructures the plugins into a
# map from plugin types to maps of plugin IDs to their values.
Expand Down
2 changes: 2 additions & 0 deletions src/meltano/core/meltano_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
class MeltanoFile(Canonical):
"""Data and loading methods for meltano.yml files."""

version: int

def __init__(
self,
version: int = VERSION,
Expand Down
42 changes: 26 additions & 16 deletions src/meltano/core/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def __init__(
os.getenv(PROJECT_SYS_DIR_ROOT_ENV, self.root / ".meltano"),
).resolve()

def refresh(self, **kwargs) -> None: # noqa: ANN003
def refresh(self, **kwargs: t.Any) -> None:
"""Refresh the project instance to reflect external changes.
This should be called whenever env vars change, project files change,
Expand Down Expand Up @@ -128,7 +128,7 @@ def refresh(self, **kwargs) -> None: # noqa: ANN003
self.__dict__.update(cls(**kwargs).__dict__)

@cached_property
def config_service(self): # noqa: ANN201
def config_service(self) -> ConfigService:
"""Get the project config service.
Returns:
Expand All @@ -146,7 +146,7 @@ def project_files(self) -> ProjectFiles:
return ProjectFiles(root=self.root, meltano_file_path=self.meltanofile)

@cached_property
def settings(self): # noqa: ANN201
def settings(self) -> ProjectSettingsService:
"""Get the project settings.
Returns:
Expand All @@ -155,7 +155,7 @@ def settings(self): # noqa: ANN201
return ProjectSettingsService(self)

@cached_property
def plugins(self): # noqa: ANN201
def plugins(self) -> ProjectPluginsService:
"""Get the project plugins.
Returns:
Expand All @@ -164,7 +164,7 @@ def plugins(self): # noqa: ANN201
return ProjectPluginsService(self)

@cached_property
def hub_service(self): # noqa: ANN201
def hub_service(self) -> MeltanoHubService:
"""Get the Meltano Hub service.
Returns:
Expand All @@ -173,11 +173,11 @@ def hub_service(self): # noqa: ANN201
return MeltanoHubService(self)

@cached_property
def _meltano_interprocess_lock(self): # noqa: ANN202
def _meltano_interprocess_lock(self) -> fasteners.InterProcessLock:
return fasteners.InterProcessLock(self.run_dir("meltano.yml.lock"))

@property
def env(self): # noqa: ANN201
def env(self) -> dict[str, str]:
"""Get environment variables for this project.
Returns:
Expand Down Expand Up @@ -250,7 +250,7 @@ def deactivate(cls) -> None:
cls._default = None

@property
def file_version(self): # noqa: ANN201
def file_version(self) -> int:
"""Get the version of Meltano found in this project's meltano.yml.
Returns:
Expand All @@ -260,7 +260,12 @@ def file_version(self): # noqa: ANN201

@classmethod
@fasteners.locked(lock="_find_lock")
def find(cls, project_root: Path | str | None = None, *, activate=True): # noqa: ANN001, ANN206
def find(
cls,
project_root: Path | str | None = None,
*,
activate: bool = True,
) -> Project:
"""Find a Project.
Args:
Expand Down Expand Up @@ -408,7 +413,7 @@ def deactivate_environment(self) -> None:
self.refresh(environment=None)

@contextmanager
def dotenv_update(self): # noqa: ANN201
def dotenv_update(self) -> Generator[Path, None, None]:
"""Raise error if project is readonly.
Used in context where .env files would be updated.
Expand Down Expand Up @@ -504,7 +509,12 @@ def logs_dir(self, *joinpaths: StrPath, make_dirs: bool = True) -> Path:
return self.meltano_dir("logs", *joinpaths, make_dirs=make_dirs)

@makedirs
def job_dir(self, state_id, *joinpaths: StrPath, make_dirs: bool = True) -> Path: # noqa: ANN001
def job_dir(
self,
state_id: str,
*joinpaths: StrPath,
make_dirs: bool = True,
) -> Path:
"""Path to the `elt` directory in `.meltano/run`.
Args:
Expand All @@ -525,7 +535,7 @@ def job_dir(self, state_id, *joinpaths: StrPath, make_dirs: bool = True) -> Path
@makedirs
def job_logs_dir(
self,
state_id, # noqa: ANN001
state_id: str,
*joinpaths: StrPath,
make_dirs: bool = True,
) -> Path:
Expand Down Expand Up @@ -584,14 +594,14 @@ def root_plugins_dir(self, *joinpaths: StrPath, make_dirs: bool = True) -> Path:
return self.root_dir("plugins", *joinpaths)

@makedirs
def plugin_lock_path( # noqa: ANN201
def plugin_lock_path(
self,
plugin_type: str,
plugin_name: str,
*,
variant_name: str | None = None,
make_dirs: bool = True,
):
) -> Path:
"""Path to the project lock file.
Args:
Expand All @@ -614,7 +624,7 @@ def plugin_lock_path( # noqa: ANN201
make_dirs=make_dirs,
)

def __eq__(self, other): # noqa: ANN001, ANN204
def __eq__(self, other: object) -> bool:
"""Project equivalence check.
Args:
Expand All @@ -625,7 +635,7 @@ def __eq__(self, other): # noqa: ANN001, ANN204
"""
return self.root == getattr(other, "root", object())

def __hash__(self): # noqa: ANN204
def __hash__(self) -> int:
"""Project hash.
Returns:
Expand Down
2 changes: 1 addition & 1 deletion src/meltano/core/project_plugins_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ def update_environment_plugin(self, plugin: EnvironmentPluginConfig) -> None:
else:
environment.config.plugins[plugin.type][p_idx] = plugin

def _get_parent_from_hub(self, plugin: ProjectPlugin) -> ProjectPlugin:
def _get_parent_from_hub(self, plugin: ProjectPlugin) -> BasePlugin:
"""Get the parent plugin from the hub.
Args:
Expand Down
36 changes: 22 additions & 14 deletions src/meltano/core/select_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
if t.TYPE_CHECKING:
from sqlalchemy.orm.session import Session

from meltano.core.plugin.base import PluginRef
from meltano.core.environment import EnvironmentPluginConfig
from meltano.core.plugin.project_plugin import ProjectPlugin
from meltano.core.project import Project

Expand Down Expand Up @@ -92,32 +92,40 @@ def update(
remove: bool = False,
) -> None:
"""Update plugins' select patterns."""
plugin: PluginRef
this_pattern = self._get_pattern_string(
entities_filter,
attributes_filter,
exclude,
)

plugin: ProjectPlugin | EnvironmentPluginConfig
if self.project.environment is None:
plugin = self.extractor
self._update_plugin_select(plugin, this_pattern, remove=remove)
self.project.plugins.update_plugin(plugin)
else:
plugin = self.project.environment.get_plugin_config(
self.extractor.type,
self.extractor.name,
)
self._update_plugin_select(plugin, this_pattern, remove=remove)
self.project.plugins.update_environment_plugin(plugin)

this_pattern = self._get_pattern_string(
entities_filter,
attributes_filter,
exclude,
)
def _update_plugin_select(
self,
plugin: ProjectPlugin | EnvironmentPluginConfig,
pattern: str,
*,
remove: bool = False,
) -> None:
"""Update the plugin's select patterns."""
patterns = plugin.extras.get("select", [])
if remove:
patterns.remove(this_pattern)
patterns.remove(pattern)
else:
patterns.append(this_pattern)
plugin.extras["select"] = patterns
patterns.append(pattern)

if self.project.environment is None:
self.project.plugins.update_plugin(plugin)
else:
self.project.plugins.update_environment_plugin(plugin)
plugin.extras["select"] = patterns

@staticmethod
def _get_pattern_string(
Expand Down
24 changes: 19 additions & 5 deletions tests/meltano/cli/test_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,41 @@


class TestCliSelect:
@pytest.mark.parametrize(
"environment",
(
pytest.param(None, id="no-environment"),
pytest.param("dev", id="dev"),
),
)
@pytest.mark.usefixtures("project")
def test_update_select_pattern(self, cli_runner, tap) -> None:
def test_update_select_pattern(self, cli_runner, tap, environment) -> None:
environment_flag = () if environment is None else ("--environment", environment)
# add select pattern
result = cli_runner.invoke(
cli,
["--no-environment", "select", tap.name, "mock", "*"],
[*environment_flag, "select", tap.name, "mock", "*"],
)
assert_cli_runner(result)
# verify pattern was added
result = cli_runner.invoke(cli, ["config", "--extras", tap.name])
result = cli_runner.invoke(
cli,
[*environment_flag, "config", "--extras", tap.name],
)
assert_cli_runner(result)
json_config = json.loads(result.stdout)
assert "mock.*" in json_config["_select"]
# remove select pattern
result = cli_runner.invoke(
cli,
["--no-environment", "select", tap.name, "--rm", "mock", "*"],
[*environment_flag, "select", tap.name, "--rm", "mock", "*"],
)
assert_cli_runner(result)
# verify select pattern removed
result = cli_runner.invoke(cli, ["config", "--extras", tap.name])
result = cli_runner.invoke(
cli,
[*environment_flag, "config", "--extras", tap.name],
)
assert_cli_runner(result)
json_config = json.loads(result.stdout)
assert "mock.*" not in json_config["_select"]
Expand Down

0 comments on commit 163da2a

Please sign in to comment.