etlplus 0.13.0__tar.gz → 0.14.1__tar.gz
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.
- {etlplus-0.13.0 → etlplus-0.14.1}/CONTRIBUTING.md +1 -1
- {etlplus-0.13.0 → etlplus-0.14.1}/DEMO.md +1 -1
- {etlplus-0.13.0/etlplus.egg-info → etlplus-0.14.1}/PKG-INFO +4 -4
- {etlplus-0.13.0 → etlplus-0.14.1}/README.md +3 -3
- {etlplus-0.13.0 → etlplus-0.14.1}/docs/pipeline-guide.md +5 -5
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/README.md +1 -1
- etlplus-0.14.1/etlplus/__init__.py +18 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/__init__.py +8 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/endpoint_client.py +3 -3
- etlplus-0.13.0/etlplus/run_helpers.py → etlplus-0.14.1/etlplus/api/utils.py +121 -79
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/handlers.py +17 -7
- {etlplus-0.13.0/etlplus/validation → etlplus-0.14.1/etlplus/ops}/README.md +2 -2
- etlplus-0.14.1/etlplus/ops/__init__.py +61 -0
- {etlplus-0.13.0/etlplus → etlplus-0.14.1/etlplus/ops}/extract.py +78 -94
- {etlplus-0.13.0/etlplus → etlplus-0.14.1/etlplus/ops}/load.py +73 -93
- {etlplus-0.13.0/etlplus → etlplus-0.14.1/etlplus/ops}/run.py +140 -110
- {etlplus-0.13.0/etlplus → etlplus-0.14.1/etlplus/ops}/transform.py +75 -68
- {etlplus-0.13.0/etlplus/validation → etlplus-0.14.1/etlplus/ops}/utils.py +62 -15
- {etlplus-0.13.0/etlplus → etlplus-0.14.1/etlplus/ops}/validate.py +19 -9
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/types.py +2 -2
- {etlplus-0.13.0 → etlplus-0.14.1/etlplus.egg-info}/PKG-INFO +4 -4
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus.egg-info/SOURCES.txt +16 -16
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/README.md +2 -2
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/configs/pipeline.yml +4 -4
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/quickstart_python.py +5 -5
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/conftest.py +4 -4
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_pagination_strategy.py +2 -2
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_run.py +1 -1
- etlplus-0.13.0/tests/unit/test_u_run_helpers.py → etlplus-0.14.1/tests/unit/api/test_u_api_utils.py +17 -15
- etlplus-0.13.0/tests/unit/test_u_extract.py → etlplus-0.14.1/tests/unit/ops/test_u_ops_extract.py +12 -12
- etlplus-0.13.0/tests/unit/test_u_load.py → etlplus-0.14.1/tests/unit/ops/test_u_ops_load.py +17 -16
- etlplus-0.13.0/tests/unit/test_u_run.py → etlplus-0.14.1/tests/unit/ops/test_u_ops_run.py +4 -4
- etlplus-0.13.0/tests/unit/test_u_transform.py → etlplus-0.14.1/tests/unit/ops/test_u_ops_transform.py +65 -64
- etlplus-0.13.0/tests/unit/validation/test_u_validation_utils.py → etlplus-0.14.1/tests/unit/ops/test_u_ops_utils.py +4 -4
- etlplus-0.13.0/tests/unit/test_u_validate.py → etlplus-0.14.1/tests/unit/ops/test_u_ops_validate.py +9 -9
- etlplus-0.13.0/etlplus/__init__.py +0 -43
- etlplus-0.13.0/etlplus/validation/__init__.py +0 -44
- {etlplus-0.13.0 → etlplus-0.14.1}/.coveragerc +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/.editorconfig +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/.gitattributes +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/.github/actions/python-bootstrap/action.yml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/.github/workflows/ci.yml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/.gitignore +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/.pre-commit-config.yaml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/.ruff.toml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/CODE_OF_CONDUCT.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/LICENSE +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/MANIFEST.in +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/Makefile +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/REFERENCES.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/SECURITY.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/SUPPORT.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/docs/README.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/docs/snippets/installation_version.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/__main__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/__version__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/README.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/auth.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/config.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/errors.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/pagination/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/pagination/client.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/pagination/config.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/pagination/paginator.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/rate_limiting/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/rate_limiting/config.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/rate_limiting/rate_limiter.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/request_manager.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/retry_manager.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/transport.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/api/types.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/README.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/commands.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/constants.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/io.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/main.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/options.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/state.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/cli/types.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/README.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/connector.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/jobs.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/pipeline.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/profile.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/types.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/config/utils.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/dag.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/database/README.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/database/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/database/ddl.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/database/engine.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/database/orm.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/database/schema.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/database/types.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/enums.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/README.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/_imports.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/_io.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/accdb.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/arrow.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/avro.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/bson.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/cbor.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/cfg.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/conf.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/core.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/csv.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/dat.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/dta.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/duckdb.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/enums.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/feather.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/fwf.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/gz.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/hbs.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/hdf5.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/ini.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/ion.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/jinja2.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/json.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/log.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/mat.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/mdb.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/msgpack.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/mustache.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/nc.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/ndjson.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/numbers.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/ods.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/orc.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/parquet.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/pb.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/pbf.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/properties.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/proto.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/psv.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/rda.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/rds.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/sas7bdat.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/sav.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/sqlite.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/stub.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/sylk.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/tab.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/toml.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/tsv.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/txt.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/vm.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/wks.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/xls.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/xlsm.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/xlsx.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/xml.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/xpt.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/yaml.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/zip.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/file/zsav.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/mixins.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/py.typed +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/templates/README.md +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/templates/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/templates/ddl.sql.j2 +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/templates/view.sql.j2 +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus/utils.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus.egg-info/dependency_links.txt +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus.egg-info/entry_points.txt +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus.egg-info/requires.txt +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/etlplus.egg-info/top_level.txt +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/configs/ddl_spec.yml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/data/sample.csv +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/data/sample.json +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/data/sample.xml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/data/sample.xsd +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/examples/data/sample.yaml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/pyproject.toml +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/pytest.ini +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/setup.cfg +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/setup.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/__init__.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/conftest.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_cli.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_examples_data_parity.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_pipeline_smoke.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_pipeline_yaml_load.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_run_profile_pagination_defaults.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/integration/test_i_run_profile_rate_limit_defaults.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/conftest.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_auth.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_config.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_endpoint_client.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_mocks.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_pagination_client.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_pagination_config.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_paginator.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_rate_limit_config.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_rate_limiter.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_request_manager.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_retry_manager.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_transport.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/api/test_u_types.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/cli/conftest.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/cli/test_u_cli_handlers.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/cli/test_u_cli_io.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/cli/test_u_cli_main.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/cli/test_u_cli_state.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/config/test_u_config_utils.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/config/test_u_connector.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/config/test_u_jobs.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/config/test_u_pipeline.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/conftest.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/database/test_u_database_ddl.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/database/test_u_database_engine.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/database/test_u_database_orm.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/database/test_u_database_schema.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/file/test_u_file_core.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/file/test_u_file_enums.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/file/test_u_file_yaml.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/test_u_enums.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/test_u_main.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/test_u_mixins.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/test_u_utils.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tests/unit/test_u_version.py +0 -0
- {etlplus-0.13.0 → etlplus-0.14.1}/tools/update_demo_snippets.py +0 -0
|
@@ -119,7 +119,7 @@ Use these guidelines to decide whether a test belongs in the unit or integration
|
|
|
119
119
|
- Can use temporary files/directories, and stub network with fakes/mocks.
|
|
120
120
|
- Examples in this repo: CLI end-to-end, pipeline smoke tests, pagination strategy, runner defaults for pagination/rate limits, target URL composition.
|
|
121
121
|
|
|
122
|
-
If a test calls `etlplus.cli.main()` or `etlplus.run.run()`, it is integration by default.
|
|
122
|
+
If a test calls `etlplus.cli.main()` or `etlplus.ops.run.run()`, it is integration by default.
|
|
123
123
|
|
|
124
124
|
### Where to put tests
|
|
125
125
|
|
|
@@ -196,7 +196,7 @@ $ etlplus load transformed.json file final_output.csv
|
|
|
196
196
|
## Demo 6: Using Python API
|
|
197
197
|
|
|
198
198
|
```python
|
|
199
|
-
from etlplus import extract, validate, transform, load
|
|
199
|
+
from etlplus.ops import extract, validate, transform, load
|
|
200
200
|
|
|
201
201
|
# Extract
|
|
202
202
|
data = extract("file", "data.csv", format="csv")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: etlplus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.1
|
|
4
4
|
Summary: A Swiss Army knife for simple ETL operations
|
|
5
5
|
Home-page: https://github.com/Dagitali/ETLPlus
|
|
6
6
|
Author: ETLPlus Team
|
|
@@ -196,7 +196,7 @@ etlplus extract file examples/data/sample.csv \
|
|
|
196
196
|
[Python API](#python-api):
|
|
197
197
|
|
|
198
198
|
```python
|
|
199
|
-
from etlplus import extract, transform, validate, load
|
|
199
|
+
from etlplus.ops import extract, transform, validate, load
|
|
200
200
|
|
|
201
201
|
data = extract("file", "input.csv")
|
|
202
202
|
ops = {"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}
|
|
@@ -531,7 +531,7 @@ cat examples/data/sample.json \
|
|
|
531
531
|
Use ETLPlus as a Python library:
|
|
532
532
|
|
|
533
533
|
```python
|
|
534
|
-
from etlplus import extract, validate, transform, load
|
|
534
|
+
from etlplus.ops import extract, validate, transform, load
|
|
535
535
|
|
|
536
536
|
# Extract data
|
|
537
537
|
data = extract("file", "data.json")
|
|
@@ -726,7 +726,7 @@ We split tests into two layers:
|
|
|
726
726
|
pagination + rate limit defaults, file/API connector interactions) may touch temp files and use
|
|
727
727
|
fake clients.
|
|
728
728
|
|
|
729
|
-
If a test calls `etlplus.cli.main()` or `etlplus.run.run()` it’s integration by default.
|
|
729
|
+
If a test calls `etlplus.cli.main()` or `etlplus.ops.run.run()` it’s integration by default. Full
|
|
730
730
|
criteria: [`CONTRIBUTING.md#testing`](CONTRIBUTING.md#testing).
|
|
731
731
|
|
|
732
732
|
### Code Coverage
|
|
@@ -146,7 +146,7 @@ etlplus extract file examples/data/sample.csv \
|
|
|
146
146
|
[Python API](#python-api):
|
|
147
147
|
|
|
148
148
|
```python
|
|
149
|
-
from etlplus import extract, transform, validate, load
|
|
149
|
+
from etlplus.ops import extract, transform, validate, load
|
|
150
150
|
|
|
151
151
|
data = extract("file", "input.csv")
|
|
152
152
|
ops = {"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}
|
|
@@ -481,7 +481,7 @@ cat examples/data/sample.json \
|
|
|
481
481
|
Use ETLPlus as a Python library:
|
|
482
482
|
|
|
483
483
|
```python
|
|
484
|
-
from etlplus import extract, validate, transform, load
|
|
484
|
+
from etlplus.ops import extract, validate, transform, load
|
|
485
485
|
|
|
486
486
|
# Extract data
|
|
487
487
|
data = extract("file", "data.json")
|
|
@@ -676,7 +676,7 @@ We split tests into two layers:
|
|
|
676
676
|
pagination + rate limit defaults, file/API connector interactions) may touch temp files and use
|
|
677
677
|
fake clients.
|
|
678
678
|
|
|
679
|
-
If a test calls `etlplus.cli.main()` or `etlplus.run.run()` it’s integration by default.
|
|
679
|
+
If a test calls `etlplus.cli.main()` or `etlplus.ops.run.run()` it’s integration by default. Full
|
|
680
680
|
criteria: [`CONTRIBUTING.md#testing`](CONTRIBUTING.md#testing).
|
|
681
681
|
|
|
682
682
|
### Code Coverage
|
|
@@ -281,7 +281,7 @@ section.
|
|
|
281
281
|
|
|
282
282
|
## Validations
|
|
283
283
|
|
|
284
|
-
Validation rule sets map field names to rules, mirroring `etlplus.validate.FieldRules`:
|
|
284
|
+
Validation rule sets map field names to rules, mirroring `etlplus.ops.validate.FieldRules`:
|
|
285
285
|
|
|
286
286
|
```yaml
|
|
287
287
|
validations:
|
|
@@ -297,7 +297,7 @@ validations:
|
|
|
297
297
|
|
|
298
298
|
## Transforms
|
|
299
299
|
|
|
300
|
-
Transformation pipelines follow `etlplus.transform` shapes exactly:
|
|
300
|
+
Transformation pipelines follow `etlplus.ops.transform` shapes exactly:
|
|
301
301
|
|
|
302
302
|
```yaml
|
|
303
303
|
transforms:
|
|
@@ -431,14 +431,14 @@ Notes:
|
|
|
431
431
|
- Environment-variable substitution (e.g. `${GITHUB_TOKEN}`) is applied the same way as when loading
|
|
432
432
|
configs via the Python API.
|
|
433
433
|
- For more details on the orchestration implementation, see
|
|
434
|
-
[Runner internals: etlplus.run](run-module.md).
|
|
434
|
+
[Runner internals: etlplus.ops.run](run-module.md).
|
|
435
435
|
|
|
436
|
-
### Python: `etlplus.run.run`
|
|
436
|
+
### Python: `etlplus.ops.run.run`
|
|
437
437
|
|
|
438
438
|
To trigger a job programmatically, use the high-level runner function exposed by the package:
|
|
439
439
|
|
|
440
440
|
```python
|
|
441
|
-
from etlplus.run import run as run_job
|
|
441
|
+
from etlplus.ops.run import run as run_job
|
|
442
442
|
|
|
443
443
|
result = run_job(
|
|
444
444
|
job="file_to_file_customers",
|
|
@@ -23,7 +23,7 @@ Back to project overview: see the top-level [README](../README.md).
|
|
|
23
23
|
## Quickstart
|
|
24
24
|
|
|
25
25
|
```python
|
|
26
|
-
from etlplus import extract, validate, transform, load
|
|
26
|
+
from etlplus.ops import extract, validate, transform, load
|
|
27
27
|
|
|
28
28
|
data = extract("file", "input.csv")
|
|
29
29
|
filtered = transform(data, {"filter": {"field": "age", "op": "gt", "value": 25}})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus` package.
|
|
3
|
+
|
|
4
|
+
Top-level facade for the ETLPlus toolkit.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .__version__ import __version__
|
|
8
|
+
|
|
9
|
+
__author__ = 'ETLPlus Team'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# SECTION: EXPORTS ========================================================== #
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
'__author__',
|
|
17
|
+
'__version__',
|
|
18
|
+
]
|
|
@@ -98,6 +98,10 @@ from .types import Headers
|
|
|
98
98
|
from .types import Params
|
|
99
99
|
from .types import RequestOptions
|
|
100
100
|
from .types import Url
|
|
101
|
+
from .utils import compose_api_request_env
|
|
102
|
+
from .utils import compose_api_target_env
|
|
103
|
+
from .utils import paginate_with_client
|
|
104
|
+
from .utils import resolve_request
|
|
101
105
|
|
|
102
106
|
# SECTION: EXPORTS ========================================================== #
|
|
103
107
|
|
|
@@ -122,6 +126,10 @@ __all__ = [
|
|
|
122
126
|
'PaginationType',
|
|
123
127
|
# Functions
|
|
124
128
|
'build_http_adapter',
|
|
129
|
+
'compose_api_request_env',
|
|
130
|
+
'compose_api_target_env',
|
|
131
|
+
'paginate_with_client',
|
|
132
|
+
'resolve_request',
|
|
125
133
|
# Type Aliases
|
|
126
134
|
'CursorPaginationConfigMap',
|
|
127
135
|
'Headers',
|
|
@@ -455,7 +455,7 @@ class EndpointClient:
|
|
|
455
455
|
-------
|
|
456
456
|
JSONData
|
|
457
457
|
Parsed JSON payload or fallback structure matching
|
|
458
|
-
:func:`etlplus.extract.extract_from_api` semantics.
|
|
458
|
+
:func:`etlplus.ops.extract.extract_from_api` semantics.
|
|
459
459
|
"""
|
|
460
460
|
return self._request_manager.get(url, **kwargs)
|
|
461
461
|
|
|
@@ -479,7 +479,7 @@ class EndpointClient:
|
|
|
479
479
|
-------
|
|
480
480
|
JSONData
|
|
481
481
|
Parsed JSON payload or fallback structure matching
|
|
482
|
-
:func:`etlplus.extract.extract_from_api` semantics.
|
|
482
|
+
:func:`etlplus.ops.extract.extract_from_api` semantics.
|
|
483
483
|
"""
|
|
484
484
|
return self._request_manager.post(url, **kwargs)
|
|
485
485
|
|
|
@@ -506,7 +506,7 @@ class EndpointClient:
|
|
|
506
506
|
-------
|
|
507
507
|
JSONData
|
|
508
508
|
Parsed JSON payload or fallback structure matching
|
|
509
|
-
:func:`etlplus.extract.extract_from_api` semantics.
|
|
509
|
+
:func:`etlplus.ops.extract.extract_from_api` semantics.
|
|
510
510
|
"""
|
|
511
511
|
return self._request_manager.request(method, url, **kwargs)
|
|
512
512
|
|
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
request/load environments, pagination configs, session objects, and endpoint
|
|
6
|
-
clients. Extracted to keep ``run.py`` focused on orchestration while enabling
|
|
7
|
-
reuse and testability.
|
|
8
|
-
|
|
9
|
-
Public (re-export safe) helpers:
|
|
10
|
-
- build_pagination_cfg(pagination, overrides)
|
|
11
|
-
- build_session(cfg)
|
|
12
|
-
- compose_api_request_env(cfg, source_obj, extract_opts)
|
|
13
|
-
- compose_api_target_env(cfg, target_obj, overrides)
|
|
14
|
-
- build_endpoint_client(base_url, base_path, endpoints, env)
|
|
15
|
-
- compute_rl_sleep_seconds(rate_limit, overrides)
|
|
16
|
-
- paginate_with_client(client, endpoint_key, params, headers,
|
|
17
|
-
timeout, pagination, sleep_seconds)
|
|
18
|
-
|
|
19
|
-
Notes
|
|
20
|
-
-----
|
|
21
|
-
These helpers intentionally accept permissive ``Any``/``Mapping`` inputs to
|
|
22
|
-
avoid tight coupling with config dataclasses while keeping runtime flexible.
|
|
2
|
+
:mod:`etlplus.api.utils` module.
|
|
3
|
+
|
|
4
|
+
Shared HTTP helpers for API clients that communicate with REST endpoints.
|
|
23
5
|
"""
|
|
24
6
|
|
|
25
7
|
from __future__ import annotations
|
|
26
8
|
|
|
27
9
|
import inspect
|
|
10
|
+
from collections.abc import Callable
|
|
28
11
|
from collections.abc import Mapping
|
|
29
12
|
from typing import Any
|
|
30
13
|
from typing import TypedDict
|
|
@@ -32,24 +15,33 @@ from typing import cast
|
|
|
32
15
|
|
|
33
16
|
import requests # type: ignore[import]
|
|
34
17
|
|
|
35
|
-
from
|
|
36
|
-
from
|
|
37
|
-
from .
|
|
38
|
-
from .
|
|
39
|
-
from .
|
|
40
|
-
from .
|
|
41
|
-
from .
|
|
42
|
-
from .
|
|
43
|
-
from .
|
|
44
|
-
from .
|
|
45
|
-
from .
|
|
46
|
-
from .
|
|
47
|
-
from .types import
|
|
18
|
+
from ..enums import HttpMethod
|
|
19
|
+
from ..types import Timeout
|
|
20
|
+
from .config import ApiConfig
|
|
21
|
+
from .config import EndpointConfig
|
|
22
|
+
from .endpoint_client import EndpointClient
|
|
23
|
+
from .pagination import PaginationConfig
|
|
24
|
+
from .pagination import PaginationConfigMap
|
|
25
|
+
from .rate_limiting import RateLimitConfig
|
|
26
|
+
from .rate_limiting import RateLimitConfigMap
|
|
27
|
+
from .rate_limiting import RateLimiter
|
|
28
|
+
from .retry_manager import RetryPolicy
|
|
29
|
+
from .types import Headers
|
|
30
|
+
from .types import Params
|
|
31
|
+
from .types import Url
|
|
32
|
+
|
|
33
|
+
# SECTION: CONSTANTS ======================================================== #
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
DEFAULT_TIMEOUT: float = 10.0
|
|
37
|
+
|
|
48
38
|
|
|
49
39
|
# SECTION: EXPORTS ========================================================== #
|
|
50
40
|
|
|
51
41
|
|
|
52
42
|
__all__ = [
|
|
43
|
+
# Constants
|
|
44
|
+
'DEFAULT_TIMEOUT',
|
|
53
45
|
# Functions
|
|
54
46
|
'build_endpoint_client',
|
|
55
47
|
'build_pagination_cfg',
|
|
@@ -58,6 +50,7 @@ __all__ = [
|
|
|
58
50
|
'compose_api_target_env',
|
|
59
51
|
'compute_rl_sleep_seconds',
|
|
60
52
|
'paginate_with_client',
|
|
53
|
+
'resolve_request',
|
|
61
54
|
# Typed Dicts
|
|
62
55
|
'ApiRequestEnv',
|
|
63
56
|
'ApiTargetEnv',
|
|
@@ -68,43 +61,83 @@ __all__ = [
|
|
|
68
61
|
# SECTION: TYPED DICTS ====================================================== #
|
|
69
62
|
|
|
70
63
|
|
|
71
|
-
class
|
|
72
|
-
"""
|
|
64
|
+
class BaseApiHttpEnv(TypedDict, total=False):
|
|
65
|
+
"""
|
|
66
|
+
Common HTTP request environment for API interactions.
|
|
67
|
+
|
|
68
|
+
Fields shared by both source-side and target-side API operations.
|
|
69
|
+
"""
|
|
73
70
|
|
|
71
|
+
# Request details
|
|
74
72
|
url: Url | None
|
|
75
73
|
headers: dict[str, str]
|
|
76
74
|
timeout: Timeout
|
|
75
|
+
|
|
76
|
+
# Session
|
|
77
77
|
session: requests.Session | None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ApiRequestEnv(BaseApiHttpEnv, total=False):
|
|
81
|
+
"""
|
|
82
|
+
Composed HTTP request environment configuration for REST API sources.
|
|
83
|
+
|
|
84
|
+
Returned by :func:`compose_api_request_env` and consumed by the API extract
|
|
85
|
+
branch. Values are fully merged with endpoint/API defaults and job-level
|
|
86
|
+
overrides, preserving the original precedence and behavior.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# Client
|
|
78
90
|
use_endpoints: bool
|
|
79
91
|
base_url: str | None
|
|
80
92
|
base_path: str | None
|
|
81
93
|
endpoints_map: dict[str, str] | None
|
|
82
94
|
endpoint_key: str | None
|
|
95
|
+
|
|
96
|
+
# Request
|
|
83
97
|
params: dict[str, Any]
|
|
84
98
|
pagination: PaginationConfigMap | None
|
|
85
99
|
sleep_seconds: float
|
|
100
|
+
|
|
101
|
+
# Reliability
|
|
86
102
|
retry: RetryPolicy | None
|
|
87
103
|
retry_network_errors: bool
|
|
88
104
|
|
|
89
105
|
|
|
90
|
-
class ApiTargetEnv(
|
|
91
|
-
"""
|
|
106
|
+
class ApiTargetEnv(BaseApiHttpEnv, total=False):
|
|
107
|
+
"""
|
|
108
|
+
Composed HTTP request environment configuration for REST API targets.
|
|
109
|
+
|
|
110
|
+
Returned by :func:`compose_api_target_env` and consumed by the API load
|
|
111
|
+
branch. Values are merged from the target object, optional API/endpoint
|
|
112
|
+
reference, and job-level overrides, preserving original precedence and
|
|
113
|
+
behavior.
|
|
114
|
+
|
|
115
|
+
Notes
|
|
116
|
+
-----
|
|
117
|
+
- Precedence for inherited values matches original logic:
|
|
118
|
+
overrides -> target -> API profile defaults.
|
|
119
|
+
- Target composition does not include pagination/rate-limit/retry since
|
|
120
|
+
loads are single-request operations; only headers/timeout/session
|
|
121
|
+
apply.
|
|
122
|
+
"""
|
|
92
123
|
|
|
93
|
-
|
|
94
|
-
headers: dict[str, str]
|
|
95
|
-
timeout: Timeout
|
|
96
|
-
session: requests.Session | None
|
|
124
|
+
# Request
|
|
97
125
|
method: str | None
|
|
98
126
|
|
|
99
127
|
|
|
100
128
|
class SessionConfig(TypedDict, total=False):
|
|
101
|
-
"""
|
|
129
|
+
"""
|
|
130
|
+
Minimal session configuration schema accepted by the
|
|
131
|
+
:class:`requests.Session` runner.
|
|
132
|
+
|
|
133
|
+
Keys mirror common :class:`requests.Session` options; all are optional.
|
|
134
|
+
"""
|
|
102
135
|
|
|
103
136
|
headers: Mapping[str, Any]
|
|
104
137
|
params: Mapping[str, Any]
|
|
105
|
-
auth: Any
|
|
138
|
+
auth: Any # (user, pass) tuple or requests-compatible auth object
|
|
106
139
|
verify: bool | str
|
|
107
|
-
cert: Any
|
|
140
|
+
cert: Any # str or (cert, key)
|
|
108
141
|
proxies: Mapping[str, Any]
|
|
109
142
|
cookies: Mapping[str, Any]
|
|
110
143
|
trust_env: bool
|
|
@@ -113,9 +146,6 @@ class SessionConfig(TypedDict, total=False):
|
|
|
113
146
|
# SECTION: INTERNAL FUNCTIONS ============================================== #
|
|
114
147
|
|
|
115
148
|
|
|
116
|
-
# -- API Environment Composition -- #
|
|
117
|
-
|
|
118
|
-
|
|
119
149
|
def _get_api_cfg_and_endpoint(
|
|
120
150
|
cfg: Any,
|
|
121
151
|
api_name: str,
|
|
@@ -226,9 +256,6 @@ def _merge_session_cfg_three(
|
|
|
226
256
|
return cast(SessionConfig | None, (merged or None))
|
|
227
257
|
|
|
228
258
|
|
|
229
|
-
# -- Mapping Helpers -- #
|
|
230
|
-
|
|
231
|
-
|
|
232
259
|
def _copy_mapping(
|
|
233
260
|
mapping: Mapping[str, Any] | None,
|
|
234
261
|
) -> dict[str, Any]:
|
|
@@ -266,9 +293,6 @@ def _update_mapping(
|
|
|
266
293
|
target.update(extra)
|
|
267
294
|
|
|
268
295
|
|
|
269
|
-
# -- Session -- #
|
|
270
|
-
|
|
271
|
-
|
|
272
296
|
def _build_session_optional(
|
|
273
297
|
cfg: SessionConfig | None,
|
|
274
298
|
) -> requests.Session | None:
|
|
@@ -285,7 +309,6 @@ def _build_session_optional(
|
|
|
285
309
|
requests.Session | None
|
|
286
310
|
Configured session or ``None``.
|
|
287
311
|
"""
|
|
288
|
-
|
|
289
312
|
if isinstance(cfg, dict):
|
|
290
313
|
return build_session(cfg)
|
|
291
314
|
return None
|
|
@@ -294,9 +317,6 @@ def _build_session_optional(
|
|
|
294
317
|
# SECTION: FUNCTIONS ======================================================== #
|
|
295
318
|
|
|
296
319
|
|
|
297
|
-
# -- API Environment Composition -- #
|
|
298
|
-
|
|
299
|
-
|
|
300
320
|
def build_endpoint_client(
|
|
301
321
|
*,
|
|
302
322
|
base_url: str,
|
|
@@ -323,15 +343,7 @@ def build_endpoint_client(
|
|
|
323
343
|
EndpointClient
|
|
324
344
|
The constructed endpoint client.
|
|
325
345
|
"""
|
|
326
|
-
|
|
327
|
-
# propagate here by preferring the class on the run module if present.
|
|
328
|
-
try:
|
|
329
|
-
from . import run as run_mod # local import to avoid cycles
|
|
330
|
-
|
|
331
|
-
ClientClass = getattr(run_mod, 'EndpointClient', EndpointClient)
|
|
332
|
-
except (ImportError, AttributeError): # pragma: no cover - fallback path
|
|
333
|
-
ClientClass = EndpointClient
|
|
334
|
-
return ClientClass(
|
|
346
|
+
return EndpointClient(
|
|
335
347
|
base_url=base_url,
|
|
336
348
|
base_path=base_path,
|
|
337
349
|
endpoints=endpoints,
|
|
@@ -558,9 +570,6 @@ def compose_api_target_env(
|
|
|
558
570
|
}
|
|
559
571
|
|
|
560
572
|
|
|
561
|
-
# -- Pagination -- #
|
|
562
|
-
|
|
563
|
-
|
|
564
573
|
def build_pagination_cfg(
|
|
565
574
|
pagination: PaginationConfig | None,
|
|
566
575
|
overrides: Mapping[str, Any] | None,
|
|
@@ -667,9 +676,6 @@ def build_pagination_cfg(
|
|
|
667
676
|
return cast(PaginationConfigMap, cfg)
|
|
668
677
|
|
|
669
678
|
|
|
670
|
-
# -- Pagination Invocation -- #
|
|
671
|
-
|
|
672
|
-
|
|
673
679
|
def paginate_with_client(
|
|
674
680
|
client: Any,
|
|
675
681
|
endpoint_key: str,
|
|
@@ -727,9 +733,6 @@ def paginate_with_client(
|
|
|
727
733
|
return client.paginate(endpoint_key, **kw_pag)
|
|
728
734
|
|
|
729
735
|
|
|
730
|
-
# -- Rate Limit -- #
|
|
731
|
-
|
|
732
|
-
|
|
733
736
|
def compute_rl_sleep_seconds(
|
|
734
737
|
rate_limit: RateLimitConfig | Mapping[str, Any] | None,
|
|
735
738
|
overrides: Mapping[str, Any] | None,
|
|
@@ -782,9 +785,6 @@ def compute_rl_sleep_seconds(
|
|
|
782
785
|
)
|
|
783
786
|
|
|
784
787
|
|
|
785
|
-
# -- Session -- #
|
|
786
|
-
|
|
787
|
-
|
|
788
788
|
def build_session(
|
|
789
789
|
cfg: SessionConfig | None,
|
|
790
790
|
) -> requests.Session:
|
|
@@ -841,3 +841,45 @@ def build_session(
|
|
|
841
841
|
pass
|
|
842
842
|
|
|
843
843
|
return s
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
def resolve_request(
|
|
847
|
+
method: HttpMethod | str,
|
|
848
|
+
*,
|
|
849
|
+
session: Any | None = None,
|
|
850
|
+
timeout: Timeout = None,
|
|
851
|
+
) -> tuple[Callable[..., requests.Response], float, HttpMethod]:
|
|
852
|
+
"""
|
|
853
|
+
Resolve a request callable and effective timeout for an HTTP method.
|
|
854
|
+
|
|
855
|
+
Parameters
|
|
856
|
+
----------
|
|
857
|
+
method : HttpMethod | str
|
|
858
|
+
HTTP method to execute.
|
|
859
|
+
session : Any | None, optional
|
|
860
|
+
Requests-compatible session object. Defaults to module-level
|
|
861
|
+
``requests``.
|
|
862
|
+
timeout : Timeout, optional
|
|
863
|
+
Timeout in seconds for the request. Uses ``DEFAULT_TIMEOUT`` when
|
|
864
|
+
omitted.
|
|
865
|
+
|
|
866
|
+
Returns
|
|
867
|
+
-------
|
|
868
|
+
tuple[Callable[..., requests.Response], float, HttpMethod]
|
|
869
|
+
Tuple of (callable, timeout_seconds, resolved_method).
|
|
870
|
+
|
|
871
|
+
Raises
|
|
872
|
+
------
|
|
873
|
+
TypeError
|
|
874
|
+
If the session object does not expose the requested HTTP method.
|
|
875
|
+
"""
|
|
876
|
+
http_method = HttpMethod.coerce(method)
|
|
877
|
+
request_timeout = DEFAULT_TIMEOUT if timeout is None else timeout
|
|
878
|
+
requester = session or requests
|
|
879
|
+
request_callable = getattr(requester, http_method.value, None)
|
|
880
|
+
if not callable(request_callable):
|
|
881
|
+
raise TypeError(
|
|
882
|
+
'Session object must supply a callable '
|
|
883
|
+
f'"{http_method.value}" method',
|
|
884
|
+
)
|
|
885
|
+
return request_callable, request_timeout, http_method
|
|
@@ -18,15 +18,16 @@ from ..config import PipelineConfig
|
|
|
18
18
|
from ..config import load_pipeline_config
|
|
19
19
|
from ..database import load_table_spec
|
|
20
20
|
from ..database import render_tables
|
|
21
|
-
from ..extract import extract
|
|
22
21
|
from ..file import File
|
|
23
|
-
from ..
|
|
24
|
-
from ..
|
|
25
|
-
from ..
|
|
22
|
+
from ..file import FileFormat
|
|
23
|
+
from ..ops import extract
|
|
24
|
+
from ..ops import load
|
|
25
|
+
from ..ops import run
|
|
26
|
+
from ..ops import transform
|
|
27
|
+
from ..ops import validate
|
|
28
|
+
from ..ops.validate import FieldRules
|
|
26
29
|
from ..types import JSONData
|
|
27
30
|
from ..types import TemplateKey
|
|
28
|
-
from ..validate import FieldRules
|
|
29
|
-
from ..validate import validate
|
|
30
31
|
from . import io as cli_io
|
|
31
32
|
|
|
32
33
|
# SECTION: EXPORTS ========================================================== #
|
|
@@ -569,8 +570,17 @@ def transform_handler(
|
|
|
569
570
|
|
|
570
571
|
data = transform(payload, cast(TransformOperations, operations_payload))
|
|
571
572
|
|
|
573
|
+
# TODO: Generalize to handle non-file targets.
|
|
572
574
|
if target and target != '-':
|
|
573
|
-
|
|
575
|
+
# Convert target to Path and target_format to FileFormat if needed
|
|
576
|
+
file_path = Path(target)
|
|
577
|
+
file_format = None
|
|
578
|
+
if target_format is not None:
|
|
579
|
+
try:
|
|
580
|
+
file_format = FileFormat(target_format)
|
|
581
|
+
except ValueError:
|
|
582
|
+
file_format = None # or handle error as appropriate
|
|
583
|
+
File(file_path, file_format=file_format).write(data)
|
|
574
584
|
print(f'Data transformed and saved to {target}')
|
|
575
585
|
return 0
|
|
576
586
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# etlplus.
|
|
1
|
+
# etlplus.ops subpackage
|
|
2
2
|
|
|
3
3
|
Documentation for the `etlplus.validation` subpackage: data validation utilities and helpers.
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ Documentation for the `etlplus.validation` subpackage: data validation utilities
|
|
|
8
8
|
|
|
9
9
|
Back to project overview: see the top-level [README](../../README.md).
|
|
10
10
|
|
|
11
|
-
- [etlplus.
|
|
11
|
+
- [etlplus.ops subpackage](#etlplusops-subpackage)
|
|
12
12
|
- [Validation Features](#validation-features)
|
|
13
13
|
- [Defining Validation Rules](#defining-validation-rules)
|
|
14
14
|
- [Example: Validating Data](#example-validating-data)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus.ops` package.
|
|
3
|
+
|
|
4
|
+
Data operations helpers.
|
|
5
|
+
|
|
6
|
+
Importing :mod:`etlplus.ops` exposes the coarse-grained helpers most users care
|
|
7
|
+
about: ``extract``, ``transform``, ``load``, ``validate``, ``run``, and
|
|
8
|
+
``run_pipeline``. Each helper delegates to the richer modules under
|
|
9
|
+
``etlplus.ops.*`` while presenting a compact public API surface. Conditional
|
|
10
|
+
validation orchestration is available via
|
|
11
|
+
:func:`etlplus.ops.utils.maybe_validate`. The legacy compatibility module
|
|
12
|
+
:mod:`etlplus.ops.__init__validation` is deprecated in favor of this package.
|
|
13
|
+
|
|
14
|
+
Examples
|
|
15
|
+
--------
|
|
16
|
+
>>> from etlplus.ops import extract, transform
|
|
17
|
+
>>> raw = extract('file', 'input.json')
|
|
18
|
+
>>> curated = transform(raw, {'select': ['id', 'name']})
|
|
19
|
+
|
|
20
|
+
>>> from etlplus.ops.utils import maybe_validate
|
|
21
|
+
>>> payload = {'name': 'Alice'}
|
|
22
|
+
>>> rules = {'required': ['name']}
|
|
23
|
+
>>> def validator(data, config):
|
|
24
|
+
... missing = [field for field in config['required'] if field not in data]
|
|
25
|
+
... return {'valid': not missing, 'errors': missing, 'data': data}
|
|
26
|
+
>>> maybe_validate(
|
|
27
|
+
... payload,
|
|
28
|
+
... when='both',
|
|
29
|
+
... enabled=True,
|
|
30
|
+
... rules=rules,
|
|
31
|
+
... phase='before_transform',
|
|
32
|
+
... severity='warn',
|
|
33
|
+
... validate_fn=validator,
|
|
34
|
+
... print_json_fn=lambda message: message,
|
|
35
|
+
... )
|
|
36
|
+
{'name': 'Alice'}
|
|
37
|
+
|
|
38
|
+
See Also
|
|
39
|
+
--------
|
|
40
|
+
:mod:`etlplus.ops.run`
|
|
41
|
+
:mod:`etlplus.ops.utils`
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from .extract import extract
|
|
45
|
+
from .load import load
|
|
46
|
+
from .run import run
|
|
47
|
+
from .run import run_pipeline
|
|
48
|
+
from .transform import transform
|
|
49
|
+
from .validate import validate
|
|
50
|
+
|
|
51
|
+
# SECTION: EXPORTS ========================================================== #
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
'extract',
|
|
56
|
+
'load',
|
|
57
|
+
'run',
|
|
58
|
+
'run_pipeline',
|
|
59
|
+
'transform',
|
|
60
|
+
'validate',
|
|
61
|
+
]
|