etlplus 0.14.1__tar.gz → 0.15.0__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.14.1/etlplus.egg-info → etlplus-0.15.0}/PKG-INFO +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/README.md +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/README.md +2 -2
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/__init__.py +2 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/config.py +36 -20
- etlplus-0.15.0/etlplus/api/enums.py +51 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/client.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/rate_limiting/config.py +13 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/rate_limiting/rate_limiter.py +8 -11
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/request_manager.py +11 -6
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/transport.py +14 -2
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/types.py +7 -6
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/utils.py +92 -78
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/README.md +2 -2
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/handlers.py +2 -2
- etlplus-0.15.0/etlplus/config/README.md +50 -0
- etlplus-0.15.0/etlplus/config/__init__.py +33 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/config/types.py +0 -64
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/README.md +2 -2
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/enums.py +0 -32
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/README.md +2 -2
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/extract.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/load.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/run.py +15 -10
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/utils.py +28 -10
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/README.md +2 -2
- {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/README.md +6 -6
- {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/__init__.py +10 -23
- {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/connector.py +17 -16
- etlplus-0.15.0/etlplus/workflow/dag.py +105 -0
- {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/jobs.py +17 -11
- {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/pipeline.py +11 -3
- {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/profile.py +8 -5
- etlplus-0.15.0/etlplus/workflow/types.py +115 -0
- {etlplus-0.14.1 → etlplus-0.15.0/etlplus.egg-info}/PKG-INFO +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/SOURCES.txt +15 -9
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/conftest.py +6 -6
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_pagination_strategy.py +11 -11
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_pipeline_yaml_load.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_run.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_run_profile_pagination_defaults.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_run_profile_rate_limit_defaults.py +1 -1
- etlplus-0.15.0/tests/unit/api/test_u_api_enums.py +34 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/conftest.py +2 -2
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_handlers.py +3 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/conftest.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_load.py +1 -1
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_enums.py +0 -16
- etlplus-0.14.1/tests/unit/config/test_u_connector.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_connector.py +6 -6
- etlplus-0.14.1/tests/unit/config/test_u_jobs.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_jobs.py +3 -3
- etlplus-0.14.1/tests/unit/config/test_u_pipeline.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_pipeline.py +7 -7
- etlplus-0.14.1/tests/unit/config/test_u_config_utils.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_utils.py +3 -3
- {etlplus-0.14.1 → etlplus-0.15.0}/.coveragerc +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/.editorconfig +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/.gitattributes +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/.github/actions/python-bootstrap/action.yml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/.github/workflows/ci.yml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/.gitignore +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/.pre-commit-config.yaml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/.ruff.toml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/CODE_OF_CONDUCT.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/CONTRIBUTING.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/DEMO.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/LICENSE +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/MANIFEST.in +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/Makefile +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/README.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/REFERENCES.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/SECURITY.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/SUPPORT.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/docs/README.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/docs/pipeline-guide.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/docs/snippets/installation_version.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/__main__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/__version__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/auth.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/endpoint_client.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/errors.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/config.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/paginator.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/rate_limiting/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/retry_manager.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/commands.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/constants.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/io.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/main.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/options.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/state.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/types.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/dag.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/ddl.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/engine.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/orm.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/schema.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/types.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/_imports.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/_io.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/accdb.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/arrow.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/avro.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/bson.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/cbor.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/cfg.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/conf.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/core.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/csv.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/dat.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/dta.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/duckdb.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/enums.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/feather.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/fwf.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/gz.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/hbs.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/hdf5.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ini.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ion.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/jinja2.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/json.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/log.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/mat.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/mdb.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/msgpack.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/mustache.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/nc.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ndjson.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/numbers.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ods.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/orc.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/parquet.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/pb.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/pbf.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/properties.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/proto.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/psv.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/rda.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/rds.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sas7bdat.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sav.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sqlite.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/stub.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sylk.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/tab.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/toml.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/tsv.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/txt.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/vm.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/wks.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xls.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xlsm.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xlsx.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xml.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xpt.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/yaml.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/zip.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/zsav.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/mixins.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/README.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/transform.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/validate.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/py.typed +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/ddl.sql.j2 +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/view.sql.j2 +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/types.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/utils.py +0 -0
- {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/utils.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/dependency_links.txt +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/entry_points.txt +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/requires.txt +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/top_level.txt +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/README.md +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/configs/ddl_spec.yml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/configs/pipeline.yml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.csv +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.json +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.xml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.xsd +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.yaml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/examples/quickstart_python.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/pyproject.toml +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/pytest.ini +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/setup.cfg +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/setup.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/__init__.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/conftest.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_cli.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_examples_data_parity.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_pipeline_smoke.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/conftest.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_api_utils.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_auth.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_config.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_endpoint_client.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_mocks.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_pagination_client.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_pagination_config.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_paginator.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_rate_limit_config.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_rate_limiter.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_request_manager.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_retry_manager.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_transport.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_types.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_io.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_main.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_state.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_ddl.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_engine.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_orm.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_schema.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/file/test_u_file_core.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/file/test_u_file_enums.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/file/test_u_file_yaml.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_extract.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_run.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_transform.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_utils.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_validate.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_main.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_mixins.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_utils.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_version.py +0 -0
- {etlplus-0.14.1 → etlplus-0.15.0}/tools/update_demo_snippets.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# etlplus.api
|
|
1
|
+
# `etlplus.api` Subpackage
|
|
2
2
|
|
|
3
3
|
Documentation for the `etlplus.api` subpackage: a lightweight HTTP client and helpers for paginated
|
|
4
4
|
REST endpoints.
|
|
@@ -12,7 +12,7 @@ REST endpoints.
|
|
|
12
12
|
|
|
13
13
|
Back to project overview: see the top-level [README](../../README.md).
|
|
14
14
|
|
|
15
|
-
- [etlplus.api
|
|
15
|
+
- [`etlplus.api` Subpackage](#etlplusapi-subpackage)
|
|
16
16
|
- [Installation](#installation)
|
|
17
17
|
- [Quickstart](#quickstart)
|
|
18
18
|
- [Overriding Rate Limits Per Call](#overriding-rate-limits-per-call)
|
|
@@ -78,6 +78,7 @@ from .config import ApiConfig
|
|
|
78
78
|
from .config import ApiProfileConfig
|
|
79
79
|
from .config import EndpointConfig
|
|
80
80
|
from .endpoint_client import EndpointClient
|
|
81
|
+
from .enums import HttpMethod
|
|
81
82
|
from .pagination import CursorPaginationConfigMap
|
|
82
83
|
from .pagination import PagePaginationConfigMap
|
|
83
84
|
from .pagination import PaginationClient
|
|
@@ -123,6 +124,7 @@ __all__ = [
|
|
|
123
124
|
'RequestOptions',
|
|
124
125
|
'RetryStrategy',
|
|
125
126
|
# Enums
|
|
127
|
+
'HttpMethod',
|
|
126
128
|
'PaginationType',
|
|
127
129
|
# Functions
|
|
128
130
|
'build_http_adapter',
|
|
@@ -18,6 +18,7 @@ Notes
|
|
|
18
18
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
|
+
from collections.abc import Callable
|
|
21
22
|
from collections.abc import Mapping
|
|
22
23
|
from dataclasses import dataclass
|
|
23
24
|
from dataclasses import field
|
|
@@ -29,13 +30,13 @@ from typing import overload
|
|
|
29
30
|
from urllib.parse import urlsplit
|
|
30
31
|
from urllib.parse import urlunsplit
|
|
31
32
|
|
|
32
|
-
from ..enums import HttpMethod
|
|
33
33
|
from ..types import StrAnyMap
|
|
34
34
|
from ..types import StrStrMap
|
|
35
35
|
from ..utils import cast_str_dict
|
|
36
36
|
from ..utils import coerce_dict
|
|
37
37
|
from ..utils import maybe_mapping
|
|
38
38
|
from .endpoint_client import EndpointClient
|
|
39
|
+
from .enums import HttpMethod
|
|
39
40
|
from .pagination import PaginationConfig
|
|
40
41
|
from .rate_limiting import RateLimitConfig
|
|
41
42
|
|
|
@@ -106,6 +107,33 @@ def _effective_service_defaults(
|
|
|
106
107
|
return fallback_base, fallback_headers
|
|
107
108
|
|
|
108
109
|
|
|
110
|
+
def _freeze_mapping(
|
|
111
|
+
mapping: Mapping[Any, Any],
|
|
112
|
+
*,
|
|
113
|
+
key_cast: Callable[[Any], Any] | None = None,
|
|
114
|
+
) -> MappingProxyType:
|
|
115
|
+
"""
|
|
116
|
+
Return an immutable copy of a mapping, optionally normalizing keys.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
mapping : Mapping[Any, Any]
|
|
121
|
+
Source mapping to freeze.
|
|
122
|
+
key_cast : Callable[[Any], Any] | None, optional
|
|
123
|
+
Optional key coercion applied to each key.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
MappingProxyType
|
|
128
|
+
Read-only mapping proxy with normalized keys.
|
|
129
|
+
"""
|
|
130
|
+
if key_cast is None:
|
|
131
|
+
data = dict(mapping)
|
|
132
|
+
else:
|
|
133
|
+
data = {key_cast(key): value for key, value in mapping.items()}
|
|
134
|
+
return MappingProxyType(data)
|
|
135
|
+
|
|
136
|
+
|
|
109
137
|
def _normalize_method(
|
|
110
138
|
value: Any,
|
|
111
139
|
) -> Any | None:
|
|
@@ -232,16 +260,8 @@ class ApiProfileConfig:
|
|
|
232
260
|
# -- Magic Methods (Object Lifecycle) -- #
|
|
233
261
|
|
|
234
262
|
def __post_init__(self) -> None:
|
|
235
|
-
object.__setattr__(
|
|
236
|
-
|
|
237
|
-
'headers',
|
|
238
|
-
MappingProxyType(dict(self.headers)),
|
|
239
|
-
)
|
|
240
|
-
object.__setattr__(
|
|
241
|
-
self,
|
|
242
|
-
'auth',
|
|
243
|
-
MappingProxyType(dict(self.auth)),
|
|
244
|
-
)
|
|
263
|
+
object.__setattr__(self, 'headers', _freeze_mapping(self.headers))
|
|
264
|
+
object.__setattr__(self, 'auth', _freeze_mapping(self.auth))
|
|
245
265
|
|
|
246
266
|
# -- Class Methods -- #
|
|
247
267
|
|
|
@@ -340,20 +360,16 @@ class ApiConfig:
|
|
|
340
360
|
# -- Magic Methods (Object Lifecycle) -- #
|
|
341
361
|
|
|
342
362
|
def __post_init__(self) -> None:
|
|
343
|
-
object.__setattr__(
|
|
344
|
-
self,
|
|
345
|
-
'headers',
|
|
346
|
-
MappingProxyType(dict(self.headers)),
|
|
347
|
-
)
|
|
363
|
+
object.__setattr__(self, 'headers', _freeze_mapping(self.headers))
|
|
348
364
|
object.__setattr__(
|
|
349
365
|
self,
|
|
350
366
|
'endpoints',
|
|
351
|
-
|
|
367
|
+
_freeze_mapping(self.endpoints, key_cast=str),
|
|
352
368
|
)
|
|
353
369
|
object.__setattr__(
|
|
354
370
|
self,
|
|
355
371
|
'profiles',
|
|
356
|
-
|
|
372
|
+
_freeze_mapping(self.profiles, key_cast=str),
|
|
357
373
|
)
|
|
358
374
|
|
|
359
375
|
# -- Internal Instance Methods -- #
|
|
@@ -545,12 +561,12 @@ class EndpointConfig:
|
|
|
545
561
|
object.__setattr__(
|
|
546
562
|
self,
|
|
547
563
|
'path_params',
|
|
548
|
-
|
|
564
|
+
_freeze_mapping(self.path_params),
|
|
549
565
|
)
|
|
550
566
|
object.__setattr__(
|
|
551
567
|
self,
|
|
552
568
|
'query_params',
|
|
553
|
-
|
|
569
|
+
_freeze_mapping(self.query_params),
|
|
554
570
|
)
|
|
555
571
|
|
|
556
572
|
# -- Class Methods -- #
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus.api.enums` module.
|
|
3
|
+
|
|
4
|
+
File-specific REST API-aligned enums and helpers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ..enums import CoercibleStrEnum
|
|
10
|
+
|
|
11
|
+
# SECTION: EXPORTS ========================================================= #
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
# Enums
|
|
16
|
+
'HttpMethod',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# SECTION: ENUMS ============================================================ #
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HttpMethod(CoercibleStrEnum):
|
|
24
|
+
"""Supported HTTP verbs that accept JSON payloads."""
|
|
25
|
+
|
|
26
|
+
# -- Constants -- #
|
|
27
|
+
|
|
28
|
+
CONNECT = 'connect'
|
|
29
|
+
DELETE = 'delete'
|
|
30
|
+
GET = 'get'
|
|
31
|
+
HEAD = 'head'
|
|
32
|
+
OPTIONS = 'options'
|
|
33
|
+
PATCH = 'patch'
|
|
34
|
+
POST = 'post'
|
|
35
|
+
PUT = 'put'
|
|
36
|
+
TRACE = 'trace'
|
|
37
|
+
|
|
38
|
+
# -- Getters -- #
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def allows_body(self) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Whether the method typically allows a request body.
|
|
44
|
+
|
|
45
|
+
Notes
|
|
46
|
+
-----
|
|
47
|
+
- RFCs do not strictly forbid bodies on some other methods (e.g.,
|
|
48
|
+
``DELETE``), but many servers/clients do not expect them. We mark
|
|
49
|
+
``POST``, ``PUT``, and ``PATCH`` as True.
|
|
50
|
+
"""
|
|
51
|
+
return self in {HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.api.rate_limiting.
|
|
2
|
+
:mod:`etlplus.api.rate_limiting.config` module.
|
|
3
3
|
|
|
4
4
|
Rate limiting configuration primitives.
|
|
5
5
|
|
|
@@ -268,6 +268,18 @@ class RateLimitConfig(BoundsWarningsMixin):
|
|
|
268
268
|
) -> Self:
|
|
269
269
|
"""
|
|
270
270
|
Normalize rate-limit config and overrides into a single instance.
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
rate_limit : StrAnyMap | RateLimitConfig | None, optional
|
|
275
|
+
Base rate-limit configuration to normalize.
|
|
276
|
+
overrides : RateLimitOverrides, optional
|
|
277
|
+
Override values that take precedence over ``rate_limit``.
|
|
278
|
+
|
|
279
|
+
Returns
|
|
280
|
+
-------
|
|
281
|
+
Self
|
|
282
|
+
Normalized rate-limit configuration.
|
|
271
283
|
"""
|
|
272
284
|
normalized = _coerce_rate_limit_map(rate_limit)
|
|
273
285
|
cfg = _merge_rate_limit(normalized, overrides)
|
|
@@ -20,6 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
|
|
21
21
|
import time
|
|
22
22
|
from dataclasses import dataclass
|
|
23
|
+
from typing import Self
|
|
23
24
|
|
|
24
25
|
from ...utils import to_float
|
|
25
26
|
from ...utils import to_positive_float
|
|
@@ -143,13 +144,13 @@ class RateLimiter:
|
|
|
143
144
|
# -- Class Methods -- #
|
|
144
145
|
|
|
145
146
|
@classmethod
|
|
146
|
-
def disabled(cls) ->
|
|
147
|
+
def disabled(cls) -> Self:
|
|
147
148
|
"""
|
|
148
149
|
Create a limiter that never sleeps.
|
|
149
150
|
|
|
150
151
|
Returns
|
|
151
152
|
-------
|
|
152
|
-
|
|
153
|
+
Self
|
|
153
154
|
Instance with rate limiting disabled.
|
|
154
155
|
"""
|
|
155
156
|
return cls(sleep_seconds=0.0)
|
|
@@ -158,7 +159,7 @@ class RateLimiter:
|
|
|
158
159
|
def fixed(
|
|
159
160
|
cls,
|
|
160
161
|
seconds: float,
|
|
161
|
-
) ->
|
|
162
|
+
) -> Self:
|
|
162
163
|
"""
|
|
163
164
|
Create a limiter with a fixed non-negative delay.
|
|
164
165
|
|
|
@@ -170,7 +171,7 @@ class RateLimiter:
|
|
|
170
171
|
|
|
171
172
|
Returns
|
|
172
173
|
-------
|
|
173
|
-
|
|
174
|
+
Self
|
|
174
175
|
Instance with the specified delay.
|
|
175
176
|
"""
|
|
176
177
|
value = to_float(seconds, 0.0, minimum=0.0) or 0.0
|
|
@@ -181,7 +182,7 @@ class RateLimiter:
|
|
|
181
182
|
def from_config(
|
|
182
183
|
cls,
|
|
183
184
|
cfg: RateLimitInput,
|
|
184
|
-
) ->
|
|
185
|
+
) -> Self:
|
|
185
186
|
"""
|
|
186
187
|
Build a :class:`RateLimiter` from a configuration mapping.
|
|
187
188
|
|
|
@@ -201,12 +202,10 @@ class RateLimiter:
|
|
|
201
202
|
|
|
202
203
|
Returns
|
|
203
204
|
-------
|
|
204
|
-
|
|
205
|
+
Self
|
|
205
206
|
Instance with normalized ``sleep_seconds`` and ``max_per_sec``.
|
|
206
207
|
"""
|
|
207
208
|
config = RateLimitConfig.from_inputs(rate_limit=cfg)
|
|
208
|
-
if config is None:
|
|
209
|
-
return cls.disabled()
|
|
210
209
|
|
|
211
210
|
# RateLimiter.__post_init__ will normalize and enforce invariants.
|
|
212
211
|
return cls(**config.as_mapping())
|
|
@@ -261,6 +260,4 @@ class RateLimiter:
|
|
|
261
260
|
rate_limit=rate_limit,
|
|
262
261
|
overrides=overrides,
|
|
263
262
|
)
|
|
264
|
-
|
|
265
|
-
return 0.0
|
|
266
|
-
return float(config.sleep_seconds)
|
|
263
|
+
return float(config.sleep_seconds) if config.sleep_seconds else 0.0
|
|
@@ -14,6 +14,7 @@ from collections.abc import Sequence
|
|
|
14
14
|
from dataclasses import dataclass
|
|
15
15
|
from dataclasses import field
|
|
16
16
|
from functools import partial
|
|
17
|
+
from types import TracebackType
|
|
17
18
|
from typing import Any
|
|
18
19
|
from typing import cast
|
|
19
20
|
|
|
@@ -137,7 +138,7 @@ class RequestManager:
|
|
|
137
138
|
self,
|
|
138
139
|
exc_type: type[BaseException] | None,
|
|
139
140
|
exc: BaseException | None,
|
|
140
|
-
tb:
|
|
141
|
+
tb: TracebackType | None,
|
|
141
142
|
) -> None:
|
|
142
143
|
"""
|
|
143
144
|
Exit the runtime context and close owned sessions.
|
|
@@ -148,7 +149,7 @@ class RequestManager:
|
|
|
148
149
|
Exception type if raised, else ``None``.
|
|
149
150
|
exc : BaseException | None
|
|
150
151
|
Exception instance if raised, else ``None``.
|
|
151
|
-
tb :
|
|
152
|
+
tb : TracebackType | None
|
|
152
153
|
Traceback if an exception was raised, else ``None``.
|
|
153
154
|
"""
|
|
154
155
|
if self._ctx_session is None:
|
|
@@ -275,7 +276,7 @@ class RequestManager:
|
|
|
275
276
|
|
|
276
277
|
try:
|
|
277
278
|
policy = self.retry
|
|
278
|
-
if
|
|
279
|
+
if policy is None:
|
|
279
280
|
try:
|
|
280
281
|
return fetch(url, **call_kwargs)
|
|
281
282
|
except requests.RequestException as exc: # pragma: no cover
|
|
@@ -438,9 +439,13 @@ class RequestManager:
|
|
|
438
439
|
if isinstance(payload, dict):
|
|
439
440
|
return cast(JSONDict, payload)
|
|
440
441
|
if isinstance(payload, list):
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
442
|
+
out: list[JSONDict] = []
|
|
443
|
+
for item in payload:
|
|
444
|
+
if isinstance(item, dict):
|
|
445
|
+
out.append(cast(JSONDict, item))
|
|
446
|
+
else:
|
|
447
|
+
out.append({'value': item})
|
|
448
|
+
return cast(JSONData, out)
|
|
444
449
|
return {'value': payload}
|
|
445
450
|
return {
|
|
446
451
|
'content': response.text,
|
|
@@ -191,7 +191,19 @@ def _build_retry_value(
|
|
|
191
191
|
def _normalize_retry_kwargs(
|
|
192
192
|
retries_cfg: Mapping[str, Any],
|
|
193
193
|
) -> dict[str, Any]:
|
|
194
|
-
"""
|
|
194
|
+
"""
|
|
195
|
+
Filter and normalize urllib3 ``Retry`` kwargs from a mapping.
|
|
196
|
+
|
|
197
|
+
Parameters
|
|
198
|
+
----------
|
|
199
|
+
retries_cfg : Mapping[str, Any]
|
|
200
|
+
Raw retry configuration mapping.
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
dict[str, Any]
|
|
205
|
+
Filtered and normalized keyword arguments for ``Retry``.
|
|
206
|
+
"""
|
|
195
207
|
allowed_keys = {
|
|
196
208
|
'total',
|
|
197
209
|
'connect',
|
|
@@ -239,7 +251,7 @@ def _resolve_max_retries(
|
|
|
239
251
|
"""
|
|
240
252
|
match retries_cfg:
|
|
241
253
|
case int():
|
|
242
|
-
return retries_cfg
|
|
254
|
+
return to_maximum_int(retries_cfg, 0)
|
|
243
255
|
case Mapping():
|
|
244
256
|
try:
|
|
245
257
|
return _build_retry_value(retries_cfg)
|
|
@@ -22,6 +22,7 @@ from __future__ import annotations
|
|
|
22
22
|
from collections.abc import Callable
|
|
23
23
|
from dataclasses import dataclass
|
|
24
24
|
from typing import Any
|
|
25
|
+
from typing import Self
|
|
25
26
|
from typing import cast
|
|
26
27
|
|
|
27
28
|
from ..types import JSONData
|
|
@@ -75,9 +76,9 @@ class RequestOptions:
|
|
|
75
76
|
# -- Magic Methods (Object Lifecycle) -- #
|
|
76
77
|
|
|
77
78
|
def __post_init__(self) -> None:
|
|
78
|
-
if self.params:
|
|
79
|
+
if self.params is not None:
|
|
79
80
|
object.__setattr__(self, 'params', dict(self.params))
|
|
80
|
-
if self.headers:
|
|
81
|
+
if self.headers is not None:
|
|
81
82
|
object.__setattr__(self, 'headers', dict(self.headers))
|
|
82
83
|
|
|
83
84
|
# -- Instance Methods -- #
|
|
@@ -92,9 +93,9 @@ class RequestOptions:
|
|
|
92
93
|
Keyword arguments for ``requests`` methods.
|
|
93
94
|
"""
|
|
94
95
|
kw: dict[str, Any] = {}
|
|
95
|
-
if self.params:
|
|
96
|
+
if self.params is not None:
|
|
96
97
|
kw['params'] = dict(self.params)
|
|
97
|
-
if self.headers:
|
|
98
|
+
if self.headers is not None:
|
|
98
99
|
kw['headers'] = dict(self.headers)
|
|
99
100
|
if self.timeout is not None:
|
|
100
101
|
kw['timeout'] = self.timeout
|
|
@@ -106,7 +107,7 @@ class RequestOptions:
|
|
|
106
107
|
params: Params | None | object = _UNSET,
|
|
107
108
|
headers: Headers | None | object = _UNSET,
|
|
108
109
|
timeout: float | None | object = _UNSET,
|
|
109
|
-
) ->
|
|
110
|
+
) -> Self:
|
|
110
111
|
"""
|
|
111
112
|
Return a copy with the provided fields replaced.
|
|
112
113
|
|
|
@@ -146,7 +147,7 @@ class RequestOptions:
|
|
|
146
147
|
else:
|
|
147
148
|
next_timeout = cast(float | None, timeout)
|
|
148
149
|
|
|
149
|
-
return
|
|
150
|
+
return self.__class__(
|
|
150
151
|
params=next_params,
|
|
151
152
|
headers=next_headers,
|
|
152
153
|
timeout=next_timeout,
|