diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3af025c..e100544 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,13 +5,6 @@ All notable changes to WuttaSync will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-## v0.6.0 (2026-03-17)
-
-### Feat
-
-- add concept of (non-)default importers for handler
-- add support for Wutta <-> Wutta import/export
-
## v0.5.1 (2026-02-13)
### Fix
diff --git a/docs/api/wuttasync.cli.export_wutta.rst b/docs/api/wuttasync.cli.export_wutta.rst
deleted file mode 100644
index cb3caf8..0000000
--- a/docs/api/wuttasync.cli.export_wutta.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-
-``wuttasync.cli.export_wutta``
-==============================
-
-.. automodule:: wuttasync.cli.export_wutta
- :members:
diff --git a/docs/api/wuttasync.cli.import_wutta.rst b/docs/api/wuttasync.cli.import_wutta.rst
deleted file mode 100644
index 466a726..0000000
--- a/docs/api/wuttasync.cli.import_wutta.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-
-``wuttasync.cli.import_wutta``
-==============================
-
-.. automodule:: wuttasync.cli.import_wutta
- :members:
diff --git a/docs/api/wuttasync.conf.rst b/docs/api/wuttasync.conf.rst
deleted file mode 100644
index 7533c9f..0000000
--- a/docs/api/wuttasync.conf.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-
-``wuttasync.conf``
-==================
-
-.. automodule:: wuttasync.conf
- :members:
diff --git a/docs/index.rst b/docs/index.rst
index 36d0e25..215e892 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -74,11 +74,8 @@ cf. :doc:`rattail-manual:data/sync/index`.
api/wuttasync.cli
api/wuttasync.cli.base
api/wuttasync.cli.export_csv
- api/wuttasync.cli.export_wutta
api/wuttasync.cli.import_csv
api/wuttasync.cli.import_versions
- api/wuttasync.cli.import_wutta
- api/wuttasync.conf
api/wuttasync.emails
api/wuttasync.exporting
api/wuttasync.exporting.base
diff --git a/docs/narr/cli/builtin.rst b/docs/narr/cli/builtin.rst
index 399c2d7..5cb3123 100644
--- a/docs/narr/cli/builtin.rst
+++ b/docs/narr/cli/builtin.rst
@@ -27,18 +27,6 @@ Defined in: :mod:`wuttasync.cli.export_csv`
.. program-output:: wutta export-csv --help
-.. _wutta-export-wutta:
-
-``wutta export-wutta``
-----------------------
-
-Export data to another Wutta :term:`app database`, from the local one.
-
-Defined in: :mod:`wuttasync.cli.export_wutta`
-
-.. program-output:: wutta export-wutta --help
-
-
.. _wutta-import-csv:
``wutta import-csv``
@@ -76,15 +64,3 @@ in the :term:`app model`.
Defined in: :mod:`wuttasync.cli.import_versions`
.. program-output:: wutta import-versions --help
-
-
-.. _wutta-import-wutta:
-
-``wutta import-wutta``
-----------------------
-
-Import data from another Wutta :term:`app database`, to the local one.
-
-Defined in: :mod:`wuttasync.cli.import_wutta`
-
-.. program-output:: wutta import-wutta --help
diff --git a/pyproject.toml b/pyproject.toml
index 4e6e0a4..d3db422 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
[project]
name = "WuttaSync"
-version = "0.6.0"
+version = "0.5.1"
description = "Wutta Framework for data import/export and real-time sync"
readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@@ -30,7 +30,7 @@ dependencies = [
"makefun",
"rich",
"SQLAlchemy-Utils",
- "WuttJamaican[db]>=0.28.10",
+ "WuttJamaican[db]>=0.28.1",
]
@@ -42,15 +42,10 @@ tests = ["pylint", "pytest", "pytest-cov", "tox", "Wutta-Continuum>=0.3.0"]
[project.entry-points."wutta.app.providers"]
wuttasync = "wuttasync.app:WuttaSyncAppProvider"
-[project.entry-points."wutta.config.extensions"]
-"wuttasync" = "wuttasync.conf:WuttaSyncConfig"
-
[project.entry-points."wuttasync.importing"]
"export.to_csv.from_wutta" = "wuttasync.exporting.csv:FromWuttaToCsv"
-"export.to_wutta.from_wutta" = "wuttasync.importing.wutta:FromWuttaToWuttaExport"
"import.to_versions.from_wutta" = "wuttasync.importing.versions:FromWuttaToVersions"
"import.to_wutta.from_csv" = "wuttasync.importing.csv:FromCsvToWutta"
-"import.to_wutta.from_wutta" = "wuttasync.importing.wutta:FromWuttaToWuttaImport"
[project.entry-points."wutta.typer_imports"]
wuttasync = "wuttasync.cli"
diff --git a/src/wuttasync/app.py b/src/wuttasync/app.py
index a73b26e..0fa19fd 100644
--- a/src/wuttasync/app.py
+++ b/src/wuttasync/app.py
@@ -2,7 +2,7 @@
################################################################################
#
# WuttaSync -- Wutta Framework for data import/export and real-time sync
-# Copyright © 2024-2026 Lance Edgar
+# Copyright © 2024-2025 Lance Edgar
#
# This file is part of Wutta Framework.
#
@@ -87,32 +87,18 @@ class WuttaSyncAppProvider(AppProvider):
:returns: List of all import/export handler classes
"""
- # first load all "registered" Handler classes. note we must
- # specify lists=True since handlers from different projects
- # can be registered with the same key.
- factory_lists = load_entry_points(
- "wuttasync.importing", lists=True, ignore_errors=True
- )
+ # first load all "registered" Handler classes
+ factories = load_entry_points("wuttasync.importing", ignore_errors=True)
# organize registered classes by spec
- specs = {}
- all_factories = []
- for factories in factory_lists.values():
- for factory in factories:
- specs[factory.get_spec()] = factory
- all_factories.append(factory)
+ specs = {factory.get_spec(): factory for factory in factories.values()}
# many handlers may not be registered per se, but may be
# designated via config. so try to include those too
- seen = set()
- for factory in all_factories:
- key = factory.get_key()
- if key in seen:
- continue
- spec = self.get_designated_import_handler_spec(key)
+ for factory in factories.values():
+ spec = self.get_designated_import_handler_spec(factory.get_key())
if spec and spec not in specs:
specs[spec] = self.app.load_object(spec)
- seen.add(key)
# flatten back to simple list of classes
factories = list(specs.values())
@@ -217,26 +203,22 @@ class WuttaSyncAppProvider(AppProvider):
:param require: Set this to true if you want an error raised
when no handler is found.
- :param \\**kwargs: Remaining kwargs are passed as-is to the
- handler constructor.
-
:returns: The import/export handler instance. If no handler
is found, then ``None`` is returned, unless ``require``
param is true, in which case error is raised.
"""
# first try to fetch the handler per designated spec
- spec = self.get_designated_import_handler_spec(key)
+ spec = self.get_designated_import_handler_spec(key, **kwargs)
if spec:
factory = self.app.load_object(spec)
- return factory(self.config, **kwargs)
+ return factory(self.config)
# nothing was designated, so leverage logic which already
# sorts out which handler is "designated" for given key
designated = self.get_designated_import_handlers()
for handler in designated:
if handler.get_key() == key:
- factory = type(handler)
- return factory(self.config, **kwargs)
+ return handler
if require:
raise ValueError(f"Cannot locate import handler for key: {key}")
diff --git a/src/wuttasync/cli/__init__.py b/src/wuttasync/cli/__init__.py
index 231f072..a3fa82b 100644
--- a/src/wuttasync/cli/__init__.py
+++ b/src/wuttasync/cli/__init__.py
@@ -40,7 +40,5 @@ from .base import (
# nb. must bring in all modules for discovery to work
from . import export_csv
-from . import export_wutta
from . import import_csv
from . import import_versions
-from . import import_wutta
diff --git a/src/wuttasync/cli/base.py b/src/wuttasync/cli/base.py
index 188555e..68bb536 100644
--- a/src/wuttasync/cli/base.py
+++ b/src/wuttasync/cli/base.py
@@ -65,13 +65,7 @@ class ImportCommandHandler(GenericHandler):
:param key: Optional :term:`import/export key` to use for handler
lookup. Only used if ``import_handler`` param is not set.
- :param \\**kwargs: Remaining kwargs are passed as-is to the
- import/export handler constructor, i.e. when making the
- :attr:`import_handler`. Note that if the ``import_handler``
- *instance* is specified, these kwargs will be ignored.
-
- Typical usage for custom commands will be to provide the spec
- (please note the *colon*)::
+ Typical usage for custom commands will be to provide the spec::
handler = ImportCommandHandler(
config, "poser.importing.foo:FromFooToPoser"
@@ -87,14 +81,6 @@ class ImportCommandHandler(GenericHandler):
See also
:meth:`~wuttasync.app.WuttaSyncAppProvider.get_import_handler()`
which does the lookup by key.
-
- Additional kwargs may be specified as needed. Typically these
- should wind up as attributes on the import/export handler
- instance::
-
- handler = ImportCommandHandler(
- config, "poser.importing.foo:FromFooToPoser", dbkey="remote"
- )
"""
import_handler = None
@@ -103,22 +89,20 @@ class ImportCommandHandler(GenericHandler):
invoked when command runs. See also :meth:`run()`.
"""
- def __init__(self, config, import_handler=None, key=None, **kwargs):
+ def __init__(self, config, import_handler=None, key=None):
super().__init__(config)
if import_handler:
if isinstance(import_handler, ImportHandler):
self.import_handler = import_handler
elif callable(import_handler):
- self.import_handler = import_handler(self.config, **kwargs)
+ self.import_handler = import_handler(self.config)
else: # spec
factory = self.app.load_object(import_handler)
- self.import_handler = factory(self.config, **kwargs)
+ self.import_handler = factory(self.config)
elif key:
- self.import_handler = self.app.get_import_handler(
- key, require=True, **kwargs
- )
+ self.import_handler = self.app.get_import_handler(key, require=True)
def run(self, ctx, progress=None): # pylint: disable=unused-argument
"""
@@ -177,7 +161,7 @@ class ImportCommandHandler(GenericHandler):
# sort out which models to process
models = kw.pop("models", None)
if not models:
- models = self.import_handler.get_default_importer_keys()
+ models = list(self.import_handler.importers)
log.debug(
"%s %s for models: %s",
self.import_handler.actioning,
@@ -196,31 +180,13 @@ class ImportCommandHandler(GenericHandler):
This is what happens when command line has ``--list-models``.
"""
- all_keys = list(self.import_handler.importers)
- default_keys = [k for k in all_keys if self.import_handler.is_default(k)]
- extra_keys = [k for k in all_keys if k not in default_keys]
-
- sys.stdout.write("\n")
+ sys.stdout.write("\nALL MODELS:\n")
sys.stdout.write("==============================\n")
- sys.stdout.write(" DEFAULT MODELS:\n")
+ for key in self.import_handler.importers:
+ sys.stdout.write(key)
+ sys.stdout.write("\n")
sys.stdout.write("==============================\n")
- if default_keys:
- for key in default_keys:
- sys.stdout.write(f"{key}\n")
- else:
- sys.stdout.write("(none)\n")
-
- sys.stdout.write("==============================\n")
- sys.stdout.write(" EXTRA MODELS:\n")
- sys.stdout.write("==============================\n")
- if extra_keys:
- for key in extra_keys:
- sys.stdout.write(f"{key}\n")
- else:
- sys.stdout.write("(none)\n")
-
- sys.stdout.write("==============================\n")
- sys.stdout.write(f" for {self.import_handler.get_title()}\n\n")
+ sys.stdout.write(f"for {self.import_handler.get_title()}\n\n")
def import_command_template( # pylint: disable=unused-argument,too-many-arguments,too-many-positional-arguments,too-many-locals
diff --git a/src/wuttasync/cli/export_wutta.py b/src/wuttasync/cli/export_wutta.py
deleted file mode 100644
index 2c89b3e..0000000
--- a/src/wuttasync/cli/export_wutta.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# WuttaSync -- Wutta Framework for data import/export and real-time sync
-# Copyright © 2024-2026 Lance Edgar
-#
-# This file is part of Wutta Framework.
-#
-# Wutta Framework is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option) any
-# later version.
-#
-# Wutta Framework is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Wutta Framework. If not, see .
-#
-################################################################################
-"""
-See also: :ref:`wutta-export-wutta`
-"""
-
-from typing_extensions import Annotated
-
-import typer
-
-from wuttjamaican.cli import wutta_typer
-
-from .base import import_command, ImportCommandHandler
-
-
-@wutta_typer.command()
-@import_command
-def export_wutta(
- ctx: typer.Context,
- dbkey: Annotated[
- str,
- typer.Option(help="Config key for app db engine to be used as data target."),
- ] = None,
- **kwargs,
-): # pylint: disable=unused-argument
- """
- Export data to another Wutta DB
- """
- config = ctx.parent.wutta_config
- handler = ImportCommandHandler(
- config, key="export.to_wutta.from_wutta", dbkey=ctx.params["dbkey"]
- )
- handler.run(ctx)
diff --git a/src/wuttasync/cli/import_wutta.py b/src/wuttasync/cli/import_wutta.py
deleted file mode 100644
index 02101e0..0000000
--- a/src/wuttasync/cli/import_wutta.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# WuttaSync -- Wutta Framework for data import/export and real-time sync
-# Copyright © 2024-2026 Lance Edgar
-#
-# This file is part of Wutta Framework.
-#
-# Wutta Framework is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option) any
-# later version.
-#
-# Wutta Framework is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Wutta Framework. If not, see .
-#
-################################################################################
-"""
-See also: :ref:`wutta-import-wutta`
-"""
-
-from typing_extensions import Annotated
-
-import typer
-
-from wuttjamaican.cli import wutta_typer
-
-from .base import import_command, ImportCommandHandler
-
-
-@wutta_typer.command()
-@import_command
-def import_wutta(
- ctx: typer.Context,
- dbkey: Annotated[
- str,
- typer.Option(help="Config key for app db engine to be used as data source."),
- ] = None,
- **kwargs,
-): # pylint: disable=unused-argument
- """
- Import data from another Wutta DB
- """
- config = ctx.parent.wutta_config
- handler = ImportCommandHandler(
- config, key="import.to_wutta.from_wutta", dbkey=ctx.params["dbkey"]
- )
- handler.run(ctx)
diff --git a/src/wuttasync/conf.py b/src/wuttasync/conf.py
deleted file mode 100644
index 3a980d5..0000000
--- a/src/wuttasync/conf.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# WuttaSync -- Wutta Framework for data import/export and real-time sync
-# Copyright © 2024-2026 Lance Edgar
-#
-# This file is part of Wutta Framework.
-#
-# Wutta Framework is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option) any
-# later version.
-#
-# Wutta Framework is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Wutta Framework. If not, see .
-#
-################################################################################
-"""
-WuttaSync config extension
-"""
-
-from wuttjamaican.conf import WuttaConfigExtension
-
-
-class WuttaSyncConfig(WuttaConfigExtension):
- """
- Config extension for WuttaSync.
-
- This just configures some default import/export handlers.
- """
-
- key = "wuttasync"
-
- def configure(self, config): # pylint: disable=empty-docstring
- """ """
-
- # default import/export handlers
- config.setdefault(
- "wuttasync.importing.import.to_wutta.from_wutta.default_handler",
- "wuttasync.importing.wutta:FromWuttaToWuttaImport",
- )
- config.setdefault(
- "wuttasync.importing.export.to_wutta.from_wutta.default_handler",
- "wuttasync.importing.wutta:FromWuttaToWuttaExport",
- )
diff --git a/src/wuttasync/emails.py b/src/wuttasync/emails.py
index 0c96f4f..b34112d 100644
--- a/src/wuttasync/emails.py
+++ b/src/wuttasync/emails.py
@@ -2,7 +2,7 @@
################################################################################
#
# WuttaSync -- Wutta Framework for data import/export and real-time sync
-# Copyright © 2024-2026 Lance Edgar
+# Copyright © 2024-2025 Lance Edgar
#
# This file is part of Wutta Framework.
#
@@ -150,14 +150,6 @@ class ImportExportWarning(EmailSetting):
}
-class export_to_wutta_from_wutta_warning( # pylint: disable=invalid-name
- ImportExportWarning
-):
- """
- Diff warning for Wutta → Wutta export.
- """
-
-
class import_to_versions_from_wutta_warning( # pylint: disable=invalid-name
ImportExportWarning
):
@@ -172,11 +164,3 @@ class import_to_wutta_from_csv_warning( # pylint: disable=invalid-name
"""
Diff warning for CSV → Wutta import.
"""
-
-
-class import_to_wutta_from_wutta_warning( # pylint: disable=invalid-name
- ImportExportWarning
-):
- """
- Diff warning for Wutta → Wutta import.
- """
diff --git a/src/wuttasync/importing/csv.py b/src/wuttasync/importing/csv.py
index ca39830..60c51eb 100644
--- a/src/wuttasync/importing/csv.py
+++ b/src/wuttasync/importing/csv.py
@@ -239,7 +239,7 @@ class FromCsvToSqlalchemyHandlerMixin:
raise NotImplementedError
# TODO: pylint (correctly) flags this as duplicate code, matching
- # on the wuttasync.importing.versions/wutta module - should fix?
+ # on the wuttasync.importing.versions module - should fix?
def define_importers(self):
"""
This mixin overrides typical (manual) importer definition, and
diff --git a/src/wuttasync/importing/handlers.py b/src/wuttasync/importing/handlers.py
index 2aa8ffe..cc53bdf 100644
--- a/src/wuttasync/importing/handlers.py
+++ b/src/wuttasync/importing/handlers.py
@@ -2,7 +2,7 @@
################################################################################
#
# WuttaSync -- Wutta Framework for data import/export and real-time sync
-# Copyright © 2024-2026 Lance Edgar
+# Copyright © 2024-2025 Lance Edgar
#
# This file is part of Wutta Framework.
#
@@ -203,12 +203,7 @@ class ImportHandler( # pylint: disable=too-many-public-methods,too-many-instanc
def __init__(self, config, **kwargs):
""" """
- super().__init__(config)
-
- # callers can set any attrs they want
- for k, v in kwargs.items():
- setattr(self, k, v)
-
+ super().__init__(config, **kwargs)
self.importers = self.define_importers()
def __str__(self):
@@ -371,7 +366,7 @@ class ImportHandler( # pylint: disable=too-many-public-methods,too-many-instanc
changes = OrderedDict()
if not keys:
- keys = self.get_default_importer_keys()
+ keys = list(self.importers)
success = False
try:
@@ -655,36 +650,6 @@ class ImportHandler( # pylint: disable=too-many-public-methods,too-many-instanc
"""
return kwargs
- def is_default(self, key):
- """
- Return a boolean indicating whether the importer corresponding
- to ``key`` should be considered "default" - i.e. included as
- part of a typical "import all" job.
-
- The default logic here returns ``True`` in all cases; subclass can
- override as needed.
-
- :param key: Key indicating the importer.
-
- :rtype: bool
- """
- return True
-
- def get_default_importer_keys(self):
- """
- Return the list of importer keys which should be considered
- "default" - i.e. which should be included as part of a typical
- "import all" job.
-
- This inspects :attr:`importers` and calls :meth:`is_default()`
- for each, to determine the result.
-
- :returns: List of importer keys (strings).
- """
- keys = list(self.importers)
- keys = [k for k in keys if self.is_default(k)]
- return keys
-
def process_changes(self, changes):
"""
Run post-processing operations on the given changes, if
diff --git a/src/wuttasync/importing/versions.py b/src/wuttasync/importing/versions.py
index 07a03a3..d558c36 100644
--- a/src/wuttasync/importing/versions.py
+++ b/src/wuttasync/importing/versions.py
@@ -138,7 +138,7 @@ class FromWuttaToVersions(FromWuttaHandler, ToWuttaHandler):
return kwargs
# TODO: pylint (correctly) flags this as duplicate code, matching
- # on the wuttasync.importing.csv/wutta module - should fix?
+ # on the wuttasync.importing.csv module - should fix?
def define_importers(self):
"""
This overrides typical (manual) importer definition, instead
diff --git a/src/wuttasync/importing/wutta.py b/src/wuttasync/importing/wutta.py
index 10e6e6c..882f7df 100644
--- a/src/wuttasync/importing/wutta.py
+++ b/src/wuttasync/importing/wutta.py
@@ -2,7 +2,7 @@
################################################################################
#
# WuttaSync -- Wutta Framework for data import/export and real-time sync
-# Copyright © 2024-2026 Lance Edgar
+# Copyright © 2024-2025 Lance Edgar
#
# This file is part of Wutta Framework.
#
@@ -24,172 +24,10 @@
Wutta → Wutta import/export
"""
-from collections import OrderedDict
-
-from sqlalchemy_utils.functions import get_primary_keys
-
-from wuttjamaican.db.util import make_topo_sortkey
-
from .base import FromSqlalchemyMirror
-from .model import ToWutta
-from .handlers import FromWuttaHandler, ToWuttaHandler, Orientation
class FromWuttaMirror(FromSqlalchemyMirror): # pylint: disable=abstract-method
"""
- Base class for Wutta → Wutta data :term:`importers/exporters
- `.
-
- This inherits from
- :class:`~wuttasync.importing.base.FromSqlalchemyMirror`.
+ Base class for Wutta -> Wutta data importers.
"""
-
-
-class FromWuttaToWuttaBase(FromWuttaHandler, ToWuttaHandler):
- """
- Base class for Wutta → Wutta data :term:`import/export handlers
- `.
-
- This inherits from
- :class:`~wuttasync.importing.handlers.FromWuttaHandler` and
- :class:`~wuttasync.importing.handlers.ToWuttaHandler`.
- """
-
- dbkey = None
- """
- Config key for the "other" (non-local) :term:`app database`.
- Depending on context this will represent either the source or
- target for import/export.
- """
-
- def get_target_model(self): # pylint: disable=missing-function-docstring
- return self.app.model
-
- # TODO: pylint (correctly) flags this as duplicate code, matching
- # on the wuttasync.importing.csv/versions module - should fix?
- def define_importers(self):
- """
- This overrides typical (manual) importer definition, and
- instead dynamically generates a set of importers, e.g. one per
- table in the target DB.
-
- It does this by calling :meth:`make_importer_factory()` for
- each class found in the :term:`app model`.
- """
- importers = {}
- model = self.get_target_model()
-
- # pylint: disable=duplicate-code
- # mostly try to make an importer for every data model
- for name in dir(model):
- cls = getattr(model, name)
- if (
- isinstance(cls, type)
- and issubclass(cls, model.Base)
- and cls is not model.Base
- ):
- importers[name] = self.make_importer_factory(cls, name)
-
- # sort importers according to schema topography
- topo_sortkey = make_topo_sortkey(model)
- importers = OrderedDict(
- [(name, importers[name]) for name in sorted(importers, key=topo_sortkey)]
- )
-
- return importers
-
- def make_importer_factory(self, model_class, name):
- """
- Generate and return a new :term:`importer` class, targeting
- the given :term:`data model` class.
-
- The newly-created class will inherit from:
-
- * :class:`FromWuttaMirror`
- * :class:`~wuttasync.importing.model.ToWutta`
-
- :param model_class: A data model class.
-
- :param name: The "model name" for the importer/exporter. New
- class name will be based on this, so e.g. ``Widget`` model
- name becomes ``WidgetImporter`` class name.
-
- :returns: The new class, meant to process import/export
- targeting the given data model.
- """
- return type(
- f"{name}Importer",
- (FromWuttaMirror, ToWutta),
- {
- "model_class": model_class,
- "key": list(get_primary_keys(model_class)),
- },
- )
-
- def is_default(self, key):
- """ """
- special = [
- "Setting",
- "Role",
- "Permission",
- "User",
- "UserRole",
- "UserAPIToken",
- "Upgrade",
- ]
- if key in special:
- return False
-
- return True
-
-
-class FromWuttaToWuttaImport(FromWuttaToWuttaBase):
- """
- Handler for Wutta (other) → Wutta (local) data import.
-
- This inherits from :class:`FromWuttaToWuttaBase`.
- """
-
- orientation = Orientation.IMPORT
- """ """ # nb. suppress docs
-
- def make_source_session(self):
- """
- This makes a "normal" :term:`db session`, but will use the
- engine corresponding to the
- :attr:`~FromWuttaToWuttaBase.dbkey`.
- """
- if (
- not self.dbkey
- or self.dbkey == "default"
- or self.dbkey not in self.config.appdb_engines
- ):
- raise ValueError(f"dbkey is not valid: {self.dbkey}")
- engine = self.config.appdb_engines[self.dbkey]
- return self.app.make_session(bind=engine)
-
-
-class FromWuttaToWuttaExport(FromWuttaToWuttaBase):
- """
- Handler for Wutta (local) → Wutta (other) data export.
-
- This inherits from :class:`FromWuttaToWuttaBase`.
- """
-
- orientation = Orientation.EXPORT
- """ """ # nb. suppress docs
-
- def make_target_session(self):
- """
- This makes a "normal" :term:`db session`, but will use the
- engine corresponding to the
- :attr:`~FromWuttaToWuttaBase.dbkey`.
- """
- if (
- not self.dbkey
- or self.dbkey == "default"
- or self.dbkey not in self.config.appdb_engines
- ):
- raise ValueError(f"dbkey is not valid: {self.dbkey}")
- engine = self.config.appdb_engines[self.dbkey]
- return self.app.make_session(bind=engine)
diff --git a/tests/cli/test_base.py b/tests/cli/test_base.py
index d2b98e6..209dbca 100644
--- a/tests/cli/test_base.py
+++ b/tests/cli/test_base.py
@@ -3,7 +3,7 @@
import inspect
import sys
from unittest import TestCase
-from unittest.mock import patch, Mock, call
+from unittest.mock import patch, Mock
from wuttasync.cli import base as mod
from wuttjamaican.testing import DataTestCase
@@ -25,55 +25,19 @@ class TestImportCommandHandler(DataTestCase):
# as spec
handler = self.make_handler(import_handler=FromCsvToWutta.get_spec())
self.assertIsInstance(handler.import_handler, FromCsvToWutta)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertFalse(hasattr(handler.import_handler, "foo"))
-
- # as spec, w/ kwargs
- handler = self.make_handler(import_handler=FromCsvToWutta.get_spec(), foo="bar")
- self.assertIsInstance(handler.import_handler, FromCsvToWutta)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertTrue(hasattr(handler.import_handler, "foo"))
- self.assertEqual(handler.import_handler.foo, "bar")
# as factory
handler = self.make_handler(import_handler=FromCsvToWutta)
self.assertIsInstance(handler.import_handler, FromCsvToWutta)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertFalse(hasattr(handler.import_handler, "foo"))
-
- # as factory, w/ kwargs
- handler = self.make_handler(import_handler=FromCsvToWutta, foo="bar")
- self.assertIsInstance(handler.import_handler, FromCsvToWutta)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertTrue(hasattr(handler.import_handler, "foo"))
- self.assertEqual(handler.import_handler.foo, "bar")
# as instance
myhandler = FromCsvToWutta(self.config)
handler = self.make_handler(import_handler=myhandler)
self.assertIs(handler.import_handler, myhandler)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertFalse(hasattr(handler.import_handler, "foo"))
-
- # as instance, w/ kwargs (which are ignored)
- myhandler = FromCsvToWutta(self.config)
- handler = self.make_handler(import_handler=myhandler, foo="bar")
- self.assertIs(handler.import_handler, myhandler)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertFalse(hasattr(handler.import_handler, "foo"))
# as key
handler = self.make_handler(key="import.to_wutta.from_csv")
self.assertIsInstance(handler.import_handler, FromCsvToWutta)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertFalse(hasattr(handler.import_handler, "foo"))
-
- # as key, w/ kwargs
- handler = self.make_handler(key="import.to_wutta.from_csv", foo="bar")
- self.assertIsInstance(handler.import_handler, FromCsvToWutta)
- self.assertFalse(hasattr(handler, "foo"))
- self.assertTrue(hasattr(handler.import_handler, "foo"))
- self.assertEqual(handler.import_handler.foo, "bar")
def test_run(self):
handler = self.make_handler(
@@ -169,60 +133,22 @@ class TestImportCommandHandler(DataTestCase):
params={},
),
)
+ # self.assertRaises(FileNotFoundError, handler.run, ctx)
handler.run(ctx)
exit_.assert_not_called()
def test_list_models(self):
-
- # CSV -> Wutta (all importers are default)
handler = self.make_handler(
import_handler="wuttasync.importing.csv:FromCsvToWutta"
)
+
with patch.object(mod, "sys") as sys:
handler.list_models({})
- sys.stdout.write.assert_has_calls(
- [
- call("==============================\n"),
- call(" EXTRA MODELS:\n"),
- call("==============================\n"),
- call("(none)\n"),
- call("==============================\n"),
- ]
- )
-
- # Wutta -> Wutta (only Person importer is default)
- handler = self.make_handler(
- import_handler="wuttasync.importing.wutta:FromWuttaToWuttaImport"
- )
- with patch.object(mod, "sys") as sys:
- handler.list_models({})
- sys.stdout.write.assert_has_calls(
- [
- call("==============================\n"),
- call(" DEFAULT MODELS:\n"),
- call("==============================\n"),
- call("Person\n"),
- call("==============================\n"),
- call(" EXTRA MODELS:\n"),
- call("==============================\n"),
- ]
- )
-
- # again, but pretend there are no default importers
- with patch.object(handler.import_handler, "is_default", return_value=False):
- with patch.object(mod, "sys") as sys:
- handler.list_models({})
- sys.stdout.write.assert_has_calls(
- [
- call("==============================\n"),
- call(" DEFAULT MODELS:\n"),
- call("==============================\n"),
- call("(none)\n"),
- call("==============================\n"),
- call(" EXTRA MODELS:\n"),
- call("==============================\n"),
- ]
- )
+ # just test a few random things we expect to see
+ self.assertTrue(sys.stdout.write.has_call("ALL MODELS:\n"))
+ self.assertTrue(sys.stdout.write.has_call("Person"))
+ self.assertTrue(sys.stdout.write.has_call("User"))
+ self.assertTrue(sys.stdout.write.has_call("Upgrade"))
class TestImporterCommand(TestCase):
diff --git a/tests/cli/test_export_wutta.py b/tests/cli/test_export_wutta.py
deleted file mode 100644
index 73e4ab2..0000000
--- a/tests/cli/test_export_wutta.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8; -*-
-
-from unittest import TestCase
-from unittest.mock import MagicMock, patch
-
-from wuttasync.cli import export_wutta as mod, ImportCommandHandler
-
-
-class TestExportWutta(TestCase):
-
- def test_basic(self):
- params = {
- "dbkey": "another",
- "models": [],
- "create": True,
- "update": True,
- "delete": False,
- "dry_run": True,
- }
- ctx = MagicMock(params=params)
- with patch.object(ImportCommandHandler, "run") as run:
- mod.export_wutta(ctx)
- run.assert_called_once_with(ctx)
diff --git a/tests/cli/test_import_wutta.py b/tests/cli/test_import_wutta.py
deleted file mode 100644
index 3887c56..0000000
--- a/tests/cli/test_import_wutta.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8; -*-
-
-from unittest import TestCase
-from unittest.mock import MagicMock, patch
-
-from wuttasync.cli import import_wutta as mod, ImportCommandHandler
-
-
-class TestImportWutta(TestCase):
-
- def test_basic(self):
- params = {
- "dbkey": "another",
- "models": [],
- "create": True,
- "update": True,
- "delete": False,
- "dry_run": True,
- }
- ctx = MagicMock(params=params)
- with patch.object(ImportCommandHandler, "run") as run:
- mod.import_wutta(ctx)
- run.assert_called_once_with(ctx)
diff --git a/tests/importing/test_handlers.py b/tests/importing/test_handlers.py
index ad8a9a3..659cda1 100644
--- a/tests/importing/test_handlers.py
+++ b/tests/importing/test_handlers.py
@@ -7,7 +7,6 @@ from uuid import UUID
from wuttjamaican.testing import DataTestCase
from wuttasync.importing import handlers as mod, Importer, ToSqlalchemy
-from wuttasync.importing.wutta import FromWuttaToWuttaImport
class FromFooToBar(mod.ImportHandler):
@@ -20,17 +19,6 @@ class TestImportHandler(DataTestCase):
def make_handler(self, **kwargs):
return mod.ImportHandler(self.config, **kwargs)
- def test_constructor(self):
-
- # attr missing by default
- handler = self.make_handler()
- self.assertFalse(hasattr(handler, "some_foo_attr"))
-
- # but constructor can set it
- handler = self.make_handler(some_foo_attr="bar")
- self.assertTrue(hasattr(handler, "some_foo_attr"))
- self.assertEqual(handler.some_foo_attr, "bar")
-
def test_str(self):
handler = self.make_handler()
self.assertEqual(str(handler), "None → None")
@@ -254,25 +242,6 @@ class TestImportHandler(DataTestCase):
KeyError, handler.get_importer, "BunchOfNonsense", model_class=model.Setting
)
- def test_is_default(self):
- handler = self.make_handler()
- # nb. anything is considered default, by default
- self.assertTrue(handler.is_default("there_is_no_way_this_is_valid"))
-
- def test_get_default_importer_keys(self):
-
- # use handler which already has some non/default keys
- handler = FromWuttaToWuttaImport(self.config)
-
- # it supports many importers
- self.assertIn("Person", handler.importers)
- self.assertIn("User", handler.importers)
- self.assertIn("Setting", handler.importers)
-
- # but only Person is default
- keys = handler.get_default_importer_keys()
- self.assertEqual(keys, ["Person"])
-
def test_get_warnings_email_key(self):
handler = FromFooToBar(self.config)
diff --git a/tests/importing/test_wutta.py b/tests/importing/test_wutta.py
index cd43df0..1533605 100644
--- a/tests/importing/test_wutta.py
+++ b/tests/importing/test_wutta.py
@@ -1,134 +1,3 @@
# -*- coding: utf-8; -*-
-from unittest.mock import patch
-
-import sqlalchemy as sa
-
-from wuttjamaican.testing import DataTestCase
-
from wuttasync.importing import wutta as mod
-from wuttasync.importing import ToWutta
-
-
-class TestFromWuttaMirror(DataTestCase):
-
- def make_importer(self, **kwargs):
- return mod.FromWuttaMirror(self.config, **kwargs)
-
- def test_basic(self):
- importer = self.make_importer()
- self.assertIsInstance(importer, mod.FromWuttaMirror)
-
-
-class TestFromWuttaToWuttaBase(DataTestCase):
-
- def make_handler(self, **kwargs):
- return mod.FromWuttaToWuttaBase(self.config, **kwargs)
-
- def test_dbkey(self):
-
- # null by default
- handler = self.make_handler()
- self.assertIsNone(handler.dbkey)
-
- # but caller can specify
- handler = self.make_handler(dbkey="another")
- self.assertEqual(handler.dbkey, "another")
-
- def test_make_importer_factory(self):
- model = self.app.model
- handler = self.make_handler()
-
- # returns a typical importer
- factory = handler.make_importer_factory(model.User, "User")
- self.assertTrue(issubclass(factory, mod.FromWuttaMirror))
- self.assertTrue(issubclass(factory, ToWutta))
- self.assertIs(factory.model_class, model.User)
- self.assertEqual(factory.__name__, "UserImporter")
-
- def test_define_importers(self):
- handler = self.make_handler()
-
- # all models are included
- importers = handler.define_importers()
- self.assertIn("Setting", importers)
- self.assertIn("Person", importers)
- self.assertIn("Role", importers)
- self.assertIn("Permission", importers)
- self.assertIn("User", importers)
- self.assertIn("UserRole", importers)
- self.assertIn("UserAPIToken", importers)
- self.assertIn("Upgrade", importers)
- self.assertNotIn("BatchMixin", importers)
- self.assertNotIn("BatchRowMixin", importers)
- self.assertNotIn("Base", importers)
-
- # also, dependencies are implied by sort order
- models = list(importers)
- self.assertLess(models.index("Person"), models.index("User"))
- self.assertLess(models.index("User"), models.index("UserRole"))
- self.assertLess(models.index("User"), models.index("Upgrade"))
-
-
-class TestFromWuttaToWuttaImport(DataTestCase):
-
- def make_handler(self, **kwargs):
- return mod.FromWuttaToWuttaImport(self.config, **kwargs)
-
- def test_make_source_session(self):
-
- # error if null dbkey
- handler = self.make_handler()
- self.assertIsNone(handler.dbkey)
- self.assertRaises(ValueError, handler.make_source_session)
-
- # error if dbkey not found
- handler = self.make_handler(dbkey="another")
- self.assertEqual(handler.dbkey, "another")
- self.assertNotIn("another", self.config.appdb_engines)
- self.assertRaises(ValueError, handler.make_source_session)
-
- # error if dbkey is 'default'
- handler = self.make_handler(dbkey="default")
- self.assertEqual(handler.dbkey, "default")
- self.assertIn("default", self.config.appdb_engines)
- self.assertRaises(ValueError, handler.make_source_session)
-
- # expected behavior
- another_engine = sa.create_engine("sqlite://")
- handler = self.make_handler(dbkey="another")
- with patch.dict(self.config.appdb_engines, {"another": another_engine}):
- session = handler.make_source_session()
- self.assertIs(session.bind, another_engine)
-
-
-class TestFromWuttaToWuttaExport(DataTestCase):
-
- def make_handler(self, **kwargs):
- return mod.FromWuttaToWuttaExport(self.config, **kwargs)
-
- def test_make_target_session(self):
-
- # error if null dbkey
- handler = self.make_handler()
- self.assertIsNone(handler.dbkey)
- self.assertRaises(ValueError, handler.make_target_session)
-
- # error if dbkey not found
- handler = self.make_handler(dbkey="another")
- self.assertEqual(handler.dbkey, "another")
- self.assertNotIn("another", self.config.appdb_engines)
- self.assertRaises(ValueError, handler.make_target_session)
-
- # error if dbkey is 'default'
- handler = self.make_handler(dbkey="default")
- self.assertEqual(handler.dbkey, "default")
- self.assertIn("default", self.config.appdb_engines)
- self.assertRaises(ValueError, handler.make_target_session)
-
- # expected behavior
- another_engine = sa.create_engine("sqlite://")
- handler = self.make_handler(dbkey="another")
- with patch.dict(self.config.appdb_engines, {"another": another_engine}):
- session = handler.make_target_session()
- self.assertIs(session.bind, another_engine)
diff --git a/tests/test_app.py b/tests/test_app.py
index 23eb4bd..560d89d 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -46,18 +46,6 @@ class TestWuttaSyncAppProvider(ConfigTestCase):
self.assertIn(FromCsvToWutta, handlers)
self.assertIn(FromFooToBar, handlers)
- # now for something completely different..here we pretend there
- # are multiple handler entry points with same key. all should
- # be returned, including both which share the key.
- entry_points = {
- "import.to_baz.from_foo": [FromFooToBaz1, FromFooToBaz2],
- }
- with patch.object(mod, "load_entry_points", return_value=entry_points):
- handlers = self.app.get_all_import_handlers()
- self.assertEqual(len(handlers), 2)
- self.assertIn(FromFooToBaz1, handlers)
- self.assertIn(FromFooToBaz2, handlers)
-
def test_get_designated_import_handler_spec(self):
# fetch of unknown key returns none
@@ -151,14 +139,6 @@ class TestWuttaSyncAppProvider(ConfigTestCase):
handler = self.app.get_import_handler("import.to_wutta.from_csv")
self.assertIsInstance(handler, FromCsvToWutta)
self.assertIsInstance(handler, FromCsvToPoser)
- self.assertFalse(hasattr(handler, "foo_attr"))
-
- # can pass extra kwargs
- handler = self.app.get_import_handler(
- "import.to_wutta.from_csv", foo_attr="whatever"
- )
- self.assertTrue(hasattr(handler, "foo_attr"))
- self.assertEqual(handler.foo_attr, "whatever")
# unknown importer cannot be found
handler = self.app.get_import_handler("bogus")
diff --git a/tests/test_conf.py b/tests/test_conf.py
deleted file mode 100644
index eefa5b7..0000000
--- a/tests/test_conf.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8; -*-
-
-from wuttjamaican.testing import ConfigTestCase
-
-from wuttasync import conf as mod
-
-
-class TestWuttaSyncConfig(ConfigTestCase):
-
- def make_extension(self):
- return mod.WuttaSyncConfig()
-
- def test_default_import_handlers(self):
-
- # base config has no default handlers
- spec = self.config.get(
- "wuttasync.importing.import.to_wutta.from_wutta.default_handler"
- )
- self.assertIsNone(spec)
- spec = self.config.get(
- "wuttasync.importing.export.to_wutta.from_wutta.default_handler"
- )
- self.assertIsNone(spec)
-
- # extend config
- ext = self.make_extension()
- ext.configure(self.config)
-
- # config now has default handlers
- spec = self.config.get(
- "wuttasync.importing.import.to_wutta.from_wutta.default_handler"
- )
- self.assertIsNotNone(spec)
- self.assertEqual(spec, "wuttasync.importing.wutta:FromWuttaToWuttaImport")
- spec = self.config.get(
- "wuttasync.importing.export.to_wutta.from_wutta.default_handler"
- )
- self.assertIsNotNone(spec)
- self.assertEqual(spec, "wuttasync.importing.wutta:FromWuttaToWuttaExport")
diff --git a/tests/test_emails.py b/tests/test_emails.py
index bdaebed..9494753 100644
--- a/tests/test_emails.py
+++ b/tests/test_emails.py
@@ -5,7 +5,6 @@ from wuttjamaican.testing import ConfigTestCase
from wuttasync import emails as mod
from wuttasync.importing import ImportHandler
from wuttasync.testing import ImportExportWarningTestCase
-from wuttasync.conf import WuttaSyncConfig
class FromFooToWutta(ImportHandler):
@@ -75,24 +74,8 @@ class TestImportExportWarning(ConfigTestCase):
class TestEmailSettings(ImportExportWarningTestCase):
- def make_config(self, files=None, **kwargs):
- config = super().make_config(files, **kwargs)
-
- # need this to ensure default import/export handlers. since
- # behavior can vary depending on what packages are installed.
- ext = WuttaSyncConfig()
- ext.configure(config)
-
- return config
-
- def test_export_to_wutta_from_wutta_warning(self):
- self.do_test_preview("export_to_wutta_from_wutta_warning")
-
def test_import_to_versions_from_wutta_warning(self):
self.do_test_preview("import_to_versions_from_wutta_warning")
def test_import_to_wutta_from_csv_warning(self):
self.do_test_preview("import_to_wutta_from_csv_warning")
-
- def test_import_to_wutta_from_wutta_warning(self):
- self.do_test_preview("import_to_wutta_from_wutta_warning")