omniload 0.9.0__tar.gz → 0.9.2__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.
- {omniload-0.9.0 → omniload-0.9.2}/PKG-INFO +4 -4
- {omniload-0.9.0 → omniload-0.9.2}/pyproject.toml +3 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/adapter.py +2 -1
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/core.py +26 -4
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/databricks.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/dropbox.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/ftp.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/gdrive.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/hdfs.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/oci.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/oss.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/sharepoint.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/smb.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/webdav.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/webhdfs.py +4 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/impl/remote.py +15 -7
- omniload-0.9.2/src/dlt_filesystem/source/lister.py +178 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/model.py +101 -3
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/core/resource.py +1 -1
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload.egg-info/PKG-INFO +4 -4
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload.egg-info/SOURCES.txt +1 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload.egg-info/requires.txt +3 -3
- {omniload-0.9.0 → omniload-0.9.2}/LICENSE +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/LICENSE.Apache-2.0 +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/MANIFEST.in +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/NOTICE +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/README.md +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/setup.cfg +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/error.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/base.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/error.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/format/bson_codec.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/format/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/format/iterable_codec.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/format/readers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/format/registry.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/format/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/local.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/onedrive.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/fsspec/r2.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/impl/util.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/source/router.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/staging.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/target/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/target/local.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/target/model.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/target/registry.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/target/remote.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/target/util.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/target/writer.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/testing/stub.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/testing/writer.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/util/auth.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/util/fsspec.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/util/loader.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/util/python.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/dlt_filesystem/util/web.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/codec/filter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/codec/hint.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/codec/masking.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/core/factory.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/core/model.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/core/registry.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/core/router.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/error.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/main.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/model.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/py.typed +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/adjust/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/adjust/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/adjust/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/airtable/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/airtable/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/allium/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/allium/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/anthropic/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/anthropic/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/anthropic/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/applovin/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/applovin/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/applovin_max/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/applovin_max/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appsflyer/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appsflyer/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appsflyer/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appstore/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appstore/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appstore/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appstore/exceptions.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appstore/models.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/appstore/resources.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/arrow/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/arrow/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/asana/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/asana/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/asana/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/asana/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/attio/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/attio/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/attio/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/bruin/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/bruin/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/chess/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/chess/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/chess/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/chess/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/clickup/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/clickup/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/clickup/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/couchbase/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/couchbase/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/couchbase/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/csv/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/cursor/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/cursor/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/cursor/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/customer_io/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/customer_io/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/customer_io/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/docebo/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/docebo/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/docebo/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/docebo/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/dune/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/dune/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/dune/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/dynamodb/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/dynamodb/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/elasticsearch/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/elasticsearch/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/facebook_ads/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/facebook_ads/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/facebook_ads/exceptions.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/facebook_ads/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/facebook_ads/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/facebook_ads/utils.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fireflies/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fireflies/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fireflies/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fluxx/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fluxx/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fluxx/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/frankfurter/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/frankfurter/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/frankfurter/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/freshdesk/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/freshdesk/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/freshdesk/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/freshdesk/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fundraiseup/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fundraiseup/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/fundraiseup/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/github/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/github/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/github/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/github/queries.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/github/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_ads/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_ads/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_ads/field.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_ads/metrics.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_ads/predicates.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_ads/reports.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_analytics/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_analytics/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_analytics/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_sheets/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_sheets/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_sheets/helpers/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_sheets/helpers/api_calls.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/google_sheets/helpers/data_processing.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/gorgias/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/gorgias/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/gorgias/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/hostaway/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/hostaway/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/hostaway/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/http/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/http/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/http/readers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/hubspot/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/hubspot/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/hubspot/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/hubspot/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/indeed/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/indeed/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/indeed/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/influxdb/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/influxdb/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/influxdb/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/intercom/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/intercom/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/intercom/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/intercom/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/isoc_pulse/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/isoc_pulse/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/jira/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/jira/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/jira/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/jira/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/kafka/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/kafka/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/kafka/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/kafka/model.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/kinesis/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/kinesis/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/kinesis/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/klaviyo/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/klaviyo/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/klaviyo/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/klaviyo/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/linear/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/linear/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/linear/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/linkedin_ads/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/linkedin_ads/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/linkedin_ads/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/linkedin_ads/model.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mailchimp/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mailchimp/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mailchimp/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mailchimp/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mixpanel/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mixpanel/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mixpanel/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/monday/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/monday/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/monday/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/monday/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mongodb/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mongodb/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mongodb/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mqbridge/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/mqbridge/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/notion/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/notion/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/notion/helpers/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/notion/helpers/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/notion/helpers/database.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/notion/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/personio/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/personio/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/personio/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/phantombuster/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/phantombuster/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/phantombuster/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pinterest/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pinterest/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pipedrive/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pipedrive/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pipedrive/helpers/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pipedrive/helpers/custom_fields_munger.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pipedrive/helpers/pages.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pipedrive/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/pipedrive/typing.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/plusvibeai/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/plusvibeai/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/plusvibeai/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/plusvibeai/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/primer/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/primer/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/primer/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/quickbooks/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/quickbooks/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/reddit_ads/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/reddit_ads/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/reddit_ads/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/revenuecat/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/revenuecat/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/revenuecat/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/rsync/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/rsync/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/rsync/command.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/rsync/config.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/rsync/error.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/rsync/stager.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/rsync/transport.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/salesforce/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/salesforce/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/salesforce/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/shopify/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/shopify/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/shopify/exceptions.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/shopify/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/shopify/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/slack/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/slack/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/slack/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/slack/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/smartsheets/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/smartsheets/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/snapchat_ads/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/snapchat_ads/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/snapchat_ads/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/snapchat_ads/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/snapchat_ads/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/socrata/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/socrata/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/socrata/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/socrata/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/solidgate/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/solidgate/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/solidgate/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/sql_database/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/sql_database/callbacks.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/sql_database/remote.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/stripe/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/stripe/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/stripe/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/stripe/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/tiktok_ads/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/tiktok_ads/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/tiktok_ads/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/trustpilot/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/trustpilot/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/trustpilot/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/wise/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/wise/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/wise/client.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zendesk/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zendesk/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zendesk/helpers/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zendesk/helpers/api_helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zendesk/helpers/credentials.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zendesk/helpers/talk_api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zendesk/settings.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zoom/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zoom/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/source/zoom/helpers.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/athena/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/bigquery/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/clickhouse/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/cratedb/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/csv/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/databricks/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/duckdb/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/elasticsearch/adapter.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/elasticsearch/api.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/model.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/mongodb/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/motherduck/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/mssql/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/mysql/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/postgresql/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/redshift/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/snowflake/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/sqlite/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/synapse/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/target/trino/__init__.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/util/auth.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/util/http.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/util/log.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/util/spinner.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload/util/time.py +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload.egg-info/dependency_links.txt +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload.egg-info/entry_points.txt +0 -0
- {omniload-0.9.0 → omniload-0.9.2}/src/omniload.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omniload
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: omniload is a polyglot data loader based on dlt. Copy data between any source and any destination.
|
|
5
5
|
Author: Mustafa Ersan, Sanju Shah Gupta, Saravjeet 'Aman' Singh
|
|
6
6
|
Author-email: Burak Karakan <burak.karakan@getbruin.com>
|
|
@@ -74,12 +74,12 @@ Requires-Dist: couchbase<5,>=4
|
|
|
74
74
|
Requires-Dist: databricks-sql-connector<5
|
|
75
75
|
Requires-Dist: databricks-sqlalchemy<3
|
|
76
76
|
Requires-Dist: dataclasses-json<0.7
|
|
77
|
-
Requires-Dist: dlt<1.
|
|
77
|
+
Requires-Dist: dlt<1.31,>=1.22
|
|
78
78
|
Requires-Dist: dlt-cratedb<0.2
|
|
79
79
|
Requires-Dist: duckdb<1.6
|
|
80
80
|
Requires-Dist: duckdb-engine<0.18
|
|
81
81
|
Requires-Dist: elasticsearch<10
|
|
82
|
-
Requires-Dist: facebook-business<
|
|
82
|
+
Requires-Dist: facebook-business<27
|
|
83
83
|
Requires-Dist: flatten-json<0.2
|
|
84
84
|
Requires-Dist: gitpython>=3.1.55
|
|
85
85
|
Requires-Dist: google-ads<32
|
|
@@ -132,7 +132,7 @@ Requires-Dist: poethepoet<1; extra == "develop"
|
|
|
132
132
|
Requires-Dist: pyproject-fmt<3; extra == "develop"
|
|
133
133
|
Requires-Dist: pytest-repeat<1; extra == "develop"
|
|
134
134
|
Requires-Dist: ruff==0.16.*; extra == "develop"
|
|
135
|
-
Requires-Dist: ty==0.0.
|
|
135
|
+
Requires-Dist: ty==0.0.70; extra == "develop"
|
|
136
136
|
Requires-Dist: validate-pyproject<1; extra == "develop"
|
|
137
137
|
Provides-Extra: filesystem
|
|
138
138
|
Requires-Dist: adlfs; extra == "filesystem"
|
|
@@ -83,12 +83,12 @@ dependencies = [
|
|
|
83
83
|
"databricks-sql-connector<5",
|
|
84
84
|
"databricks-sqlalchemy<3",
|
|
85
85
|
"dataclasses-json<0.7",
|
|
86
|
-
"dlt>=1.22,<1.
|
|
86
|
+
"dlt>=1.22,<1.31",
|
|
87
87
|
"dlt-cratedb<0.2",
|
|
88
88
|
"duckdb<1.6",
|
|
89
89
|
"duckdb-engine<0.18",
|
|
90
90
|
"elasticsearch<10",
|
|
91
|
-
"facebook-business<
|
|
91
|
+
"facebook-business<27",
|
|
92
92
|
"flatten-json<0.2",
|
|
93
93
|
"gitpython>=3.1.55",
|
|
94
94
|
"google-ads<32",
|
|
@@ -142,7 +142,7 @@ optional-dependencies.develop = [
|
|
|
142
142
|
"pyproject-fmt<3",
|
|
143
143
|
"pytest-repeat<1",
|
|
144
144
|
"ruff==0.16.*",
|
|
145
|
-
"ty==0.0.
|
|
145
|
+
"ty==0.0.70",
|
|
146
146
|
"validate-pyproject<1",
|
|
147
147
|
]
|
|
148
148
|
optional-dependencies.filesystem = [
|
|
@@ -19,7 +19,7 @@ from typing import Iterator, List, Optional, Tuple, Union
|
|
|
19
19
|
import dlt
|
|
20
20
|
from dlt.sources import DltResource
|
|
21
21
|
from dlt.sources.credentials import FileSystemCredentials
|
|
22
|
-
from dlt.sources.filesystem import FileItem, FileItemDict, fsspec_filesystem
|
|
22
|
+
from dlt.sources.filesystem import FileItem, FileItemDict, fsspec_filesystem
|
|
23
23
|
from fsspec import AbstractFileSystem
|
|
24
24
|
|
|
25
25
|
from dlt_filesystem.source.error import NoFilesFoundError
|
|
@@ -38,6 +38,7 @@ from dlt_filesystem.source.format.readers import (
|
|
|
38
38
|
read_xml,
|
|
39
39
|
read_yaml,
|
|
40
40
|
)
|
|
41
|
+
from dlt_filesystem.source.lister import glob_files
|
|
41
42
|
|
|
42
43
|
from .model import FilesystemConfigurationResource
|
|
43
44
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Union
|
|
1
|
+
from typing import Any, Optional, Union
|
|
2
2
|
|
|
3
3
|
import dlt
|
|
4
4
|
from dlt.extract import DltResource, DltSource
|
|
@@ -7,7 +7,11 @@ from fsspec import AbstractFileSystem
|
|
|
7
7
|
from dlt_filesystem.source.adapter import filesystem, readers
|
|
8
8
|
from dlt_filesystem.source.error import UnsupportedEndpointError
|
|
9
9
|
from dlt_filesystem.source.format.registry import supported_file_format_message
|
|
10
|
-
from dlt_filesystem.source.model import
|
|
10
|
+
from dlt_filesystem.source.model import (
|
|
11
|
+
FilesystemLocator,
|
|
12
|
+
FilesystemReference,
|
|
13
|
+
ResourceOptions,
|
|
14
|
+
)
|
|
11
15
|
from dlt_filesystem.source.router import determine_endpoint
|
|
12
16
|
|
|
13
17
|
|
|
@@ -60,12 +64,23 @@ def resource_for_reader(ref: FilesystemReference) -> Union[DltSource, DltResourc
|
|
|
60
64
|
|
|
61
65
|
|
|
62
66
|
def infer_resource(
|
|
63
|
-
fs: AbstractFileSystem,
|
|
67
|
+
fs: AbstractFileSystem,
|
|
68
|
+
locator: FilesystemLocator,
|
|
69
|
+
options: Optional[ResourceOptions] = None,
|
|
64
70
|
) -> Union[DltSource, DltResource]:
|
|
65
71
|
"""
|
|
66
72
|
Infer dlt resource from fsspec filesystem, with reader.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
fs: The filesystem the connector built from its own connection arguments.
|
|
76
|
+
locator: The parsed source URI.
|
|
77
|
+
options: The resource options omniload's run contributes, as split out by
|
|
78
|
+
`split_run_options`. Omitted by callers that have none, which reads the
|
|
79
|
+
same as a run that enabled nothing.
|
|
67
80
|
"""
|
|
68
81
|
|
|
82
|
+
options = options or ResourceOptions()
|
|
83
|
+
|
|
69
84
|
# Decode into base url and url path / file glob, and apply sanity checks.
|
|
70
85
|
locator.validate()
|
|
71
86
|
|
|
@@ -88,8 +103,15 @@ def infer_resource(
|
|
|
88
103
|
# concrete file. This keeps wildcard discovery empty-safe.
|
|
89
104
|
require_file_match=locator.require_file_match,
|
|
90
105
|
hints=locator.hints,
|
|
106
|
+
filesystem_incremental=options.filesystem_incremental,
|
|
91
107
|
# TODO: Can `column_types` be looped into reader|writer hints instead?
|
|
92
108
|
# We believe it represents a special case handling for `csv_headless`.
|
|
93
|
-
|
|
109
|
+
# The run value (`--columns`) wins; the URI query parameter remains the
|
|
110
|
+
# fallback for callers that address it there.
|
|
111
|
+
column_types=(
|
|
112
|
+
options.column_types
|
|
113
|
+
if options.column_types is not None
|
|
114
|
+
else locator.options.params.get("column_types")
|
|
115
|
+
),
|
|
94
116
|
)
|
|
95
117
|
)
|
|
@@ -4,7 +4,7 @@ from fsspec import AbstractFileSystem
|
|
|
4
4
|
|
|
5
5
|
from dlt_filesystem.source.base import FilesystemSource
|
|
6
6
|
from dlt_filesystem.source.core import infer_resource
|
|
7
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
7
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
8
8
|
from dlt_filesystem.util.python import (
|
|
9
9
|
cast_to_bool,
|
|
10
10
|
cast_to_int,
|
|
@@ -43,8 +43,9 @@ class DatabricksSource(FilesystemSource):
|
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
# Decode individual options (type casting, default values, sanity checks).
|
|
46
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
46
47
|
fs_kwargs = locator.options.fs_kwargs
|
|
47
|
-
fs_kwargs.update(
|
|
48
|
+
fs_kwargs.update(connector_kwargs)
|
|
48
49
|
cast_to_int(
|
|
49
50
|
fs_kwargs,
|
|
50
51
|
[
|
|
@@ -71,4 +72,4 @@ class DatabricksSource(FilesystemSource):
|
|
|
71
72
|
|
|
72
73
|
# Create filesystem and dlt resource wrapper.
|
|
73
74
|
fs = self.fs_class(**fs_kwargs)
|
|
74
|
-
return infer_resource(fs=fs, locator=locator)
|
|
75
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -4,7 +4,7 @@ from fsspec import AbstractFileSystem
|
|
|
4
4
|
|
|
5
5
|
from dlt_filesystem.source.base import FilesystemSource
|
|
6
6
|
from dlt_filesystem.source.core import infer_resource
|
|
7
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
7
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class DropboxSource(FilesystemSource):
|
|
@@ -34,9 +34,10 @@ class DropboxSource(FilesystemSource):
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
# Decode individual options (type casting, default values, sanity checks). Schema:
|
|
37
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
37
38
|
fs_kwargs = locator.options.fs_kwargs
|
|
38
|
-
fs_kwargs.update(
|
|
39
|
+
fs_kwargs.update(connector_kwargs)
|
|
39
40
|
|
|
40
41
|
# Create filesystem and dlt resource wrapper.
|
|
41
42
|
fs = self.fs_class(**fs_kwargs)
|
|
42
|
-
return infer_resource(fs=fs, locator=locator)
|
|
43
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dlt_filesystem.error import MissingConnectorOption
|
|
2
2
|
from dlt_filesystem.source.base import FilesystemSource
|
|
3
3
|
from dlt_filesystem.source.core import infer_resource
|
|
4
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
4
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
5
5
|
from dlt_filesystem.util.python import asbool, cast_to_int
|
|
6
6
|
|
|
7
7
|
|
|
@@ -23,8 +23,9 @@ class FTPSource(FilesystemSource):
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
# Decode individual options (type casting, default values, sanity checks).
|
|
26
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
26
27
|
fs_kwargs = locator.options.fs_kwargs
|
|
27
|
-
fs_kwargs.update(
|
|
28
|
+
fs_kwargs.update(connector_kwargs)
|
|
28
29
|
fs_kwargs["port"] = fs_kwargs.get("port", locator.default_port)
|
|
29
30
|
# Cast values to `int`.
|
|
30
31
|
cast_to_int(fs_kwargs, ["block_size", "port", "timeout"])
|
|
@@ -41,4 +42,4 @@ class FTPSource(FilesystemSource):
|
|
|
41
42
|
|
|
42
43
|
# Create filesystem and dlt resource wrapper.
|
|
43
44
|
fs = FTPFileSystem(**fs_kwargs)
|
|
44
|
-
return infer_resource(fs=fs, locator=locator)
|
|
45
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -4,7 +4,7 @@ from fsspec import AbstractFileSystem
|
|
|
4
4
|
|
|
5
5
|
from dlt_filesystem.source.base import FilesystemSource
|
|
6
6
|
from dlt_filesystem.source.core import infer_resource
|
|
7
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
7
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
8
8
|
from dlt_filesystem.util.python import apply_alias, cast_to_bool, cast_to_dict
|
|
9
9
|
|
|
10
10
|
|
|
@@ -37,8 +37,9 @@ class GoogleDriveSource(FilesystemSource):
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
# Decode individual options (type casting, default values, sanity checks).
|
|
40
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
40
41
|
fs_kwargs = locator.options.fs_kwargs
|
|
41
|
-
fs_kwargs.update(
|
|
42
|
+
fs_kwargs.update(connector_kwargs)
|
|
42
43
|
apply_alias(fs_kwargs, "auth", "auth_kwargs")
|
|
43
44
|
cast_to_dict(fs_kwargs, ["creds", "auth_kwargs"])
|
|
44
45
|
if "auth_kwargs" in fs_kwargs:
|
|
@@ -46,4 +47,4 @@ class GoogleDriveSource(FilesystemSource):
|
|
|
46
47
|
|
|
47
48
|
# Create filesystem and dlt resource wrapper.
|
|
48
49
|
fs = self.fs_class(**fs_kwargs)
|
|
49
|
-
return infer_resource(fs=fs, locator=locator)
|
|
50
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -5,7 +5,7 @@ from fsspec import AbstractFileSystem
|
|
|
5
5
|
from dlt_filesystem.error import MissingConnectorOption
|
|
6
6
|
from dlt_filesystem.source.base import FilesystemSource
|
|
7
7
|
from dlt_filesystem.source.core import infer_resource
|
|
8
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
8
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
9
9
|
from dlt_filesystem.util.python import apply_alias, cast_to_dict, cast_to_int
|
|
10
10
|
|
|
11
11
|
|
|
@@ -33,8 +33,9 @@ class HDFSSource(FilesystemSource):
|
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
# Decode individual options (type casting, default values, sanity checks).
|
|
36
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
36
37
|
fs_kwargs = locator.options.fs_kwargs
|
|
37
|
-
fs_kwargs.update(
|
|
38
|
+
fs_kwargs.update(connector_kwargs)
|
|
38
39
|
fs_kwargs["port"] = fs_kwargs.get("port", locator.default_port)
|
|
39
40
|
apply_alias(fs_kwargs, "block_size", "default_block_size")
|
|
40
41
|
cast_to_int(
|
|
@@ -48,4 +49,4 @@ class HDFSSource(FilesystemSource):
|
|
|
48
49
|
|
|
49
50
|
# Create filesystem and dlt resource wrapper.
|
|
50
51
|
fs = self.fs_class(**fs_kwargs)
|
|
51
|
-
return infer_resource(fs=fs, locator=locator)
|
|
52
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -4,7 +4,7 @@ from fsspec import AbstractFileSystem
|
|
|
4
4
|
|
|
5
5
|
from dlt_filesystem.source.base import FilesystemSource
|
|
6
6
|
from dlt_filesystem.source.core import infer_resource
|
|
7
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
7
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
8
8
|
from dlt_filesystem.util.python import apply_alias, cast_to_dict, cast_to_int
|
|
9
9
|
|
|
10
10
|
|
|
@@ -36,8 +36,9 @@ class OCISource(FilesystemSource):
|
|
|
36
36
|
|
|
37
37
|
# Decode individual options (type casting, default values, sanity checks). Schema:
|
|
38
38
|
# {"default_block_size": int, "config": dict, "config_kwargs": dict, "oci_additional_kwargs": dict}
|
|
39
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
39
40
|
fs_kwargs = locator.options.fs_kwargs
|
|
40
|
-
fs_kwargs.update(
|
|
41
|
+
fs_kwargs.update(connector_kwargs)
|
|
41
42
|
|
|
42
43
|
# Decode dict-typed `config`, `config_kwargs`, `oci_additional_kwargs` from JSON.
|
|
43
44
|
cast_to_dict(fs_kwargs, ["config"], on_error="ignore")
|
|
@@ -49,4 +50,4 @@ class OCISource(FilesystemSource):
|
|
|
49
50
|
|
|
50
51
|
# Create filesystem and dlt resource wrapper.
|
|
51
52
|
fs = self.fs_class(**fs_kwargs)
|
|
52
|
-
return infer_resource(fs=fs, locator=locator)
|
|
53
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -4,7 +4,7 @@ from fsspec import AbstractFileSystem
|
|
|
4
4
|
|
|
5
5
|
from dlt_filesystem.source.base import FilesystemSource
|
|
6
6
|
from dlt_filesystem.source.core import infer_resource
|
|
7
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
7
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
8
8
|
from dlt_filesystem.util.python import apply_alias, cast_to_int
|
|
9
9
|
|
|
10
10
|
|
|
@@ -35,8 +35,9 @@ class OSSSource(FilesystemSource):
|
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
# Decode individual options (type casting, default values, sanity checks).
|
|
38
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
38
39
|
fs_kwargs = locator.options.fs_kwargs
|
|
39
|
-
fs_kwargs.update(
|
|
40
|
+
fs_kwargs.update(connector_kwargs)
|
|
40
41
|
apply_alias(fs_kwargs, "block_size", "default_block_size")
|
|
41
42
|
apply_alias(fs_kwargs, "cache_type", "default_cache_type")
|
|
42
43
|
cast_to_int(fs_kwargs, ["default_block_size"])
|
|
@@ -50,4 +51,4 @@ class OSSSource(FilesystemSource):
|
|
|
50
51
|
|
|
51
52
|
# Create filesystem and dlt resource wrapper.
|
|
52
53
|
fs = self.fs_class(**fs_kwargs)
|
|
53
|
-
return infer_resource(fs=fs, locator=locator)
|
|
54
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -4,7 +4,7 @@ from fsspec import AbstractFileSystem
|
|
|
4
4
|
|
|
5
5
|
from dlt_filesystem.source.base import FilesystemSource
|
|
6
6
|
from dlt_filesystem.source.core import infer_resource
|
|
7
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
7
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
8
8
|
from dlt_filesystem.util.python import cast_to_bool, cast_to_dict
|
|
9
9
|
|
|
10
10
|
|
|
@@ -39,11 +39,12 @@ class SharePointSource(FilesystemSource):
|
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
# Decode individual options (type casting, default values, sanity checks). Schema:
|
|
42
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
42
43
|
fs_kwargs = locator.options.fs_kwargs
|
|
43
|
-
fs_kwargs.update(
|
|
44
|
+
fs_kwargs.update(connector_kwargs)
|
|
44
45
|
cast_to_dict(fs_kwargs, ["oauth2_client_params"])
|
|
45
46
|
cast_to_bool(fs_kwargs, ["use_recycle_bin"])
|
|
46
47
|
|
|
47
48
|
# Create filesystem and dlt resource wrapper.
|
|
48
49
|
fs = self.fs_class(**fs_kwargs)
|
|
49
|
-
return infer_resource(fs=fs, locator=locator)
|
|
50
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -5,7 +5,7 @@ from fsspec import AbstractFileSystem
|
|
|
5
5
|
from dlt_filesystem.error import MissingConnectorOption
|
|
6
6
|
from dlt_filesystem.source.base import FilesystemSource
|
|
7
7
|
from dlt_filesystem.source.core import infer_resource
|
|
8
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
8
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
9
9
|
from dlt_filesystem.util.python import cast_to_bool, cast_to_int
|
|
10
10
|
|
|
11
11
|
|
|
@@ -36,8 +36,9 @@ class SMBSource(FilesystemSource):
|
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
# Decode individual options (type casting, default values, sanity checks). Schema:
|
|
39
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
39
40
|
fs_kwargs = locator.options.fs_kwargs
|
|
40
|
-
fs_kwargs.update(
|
|
41
|
+
fs_kwargs.update(connector_kwargs)
|
|
41
42
|
cast_to_int(
|
|
42
43
|
fs_kwargs,
|
|
43
44
|
[
|
|
@@ -56,4 +57,4 @@ class SMBSource(FilesystemSource):
|
|
|
56
57
|
|
|
57
58
|
# Create filesystem and dlt resource wrapper.
|
|
58
59
|
fs = self.fs_class(**fs_kwargs)
|
|
59
|
-
return infer_resource(fs=fs, locator=locator)
|
|
60
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -6,7 +6,7 @@ from dlt_filesystem.error import MissingConnectorOption
|
|
|
6
6
|
from dlt_filesystem.source.base import FilesystemSource
|
|
7
7
|
from dlt_filesystem.source.core import infer_resource
|
|
8
8
|
from dlt_filesystem.source.impl.util import strip_protocol_suffix
|
|
9
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
9
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
10
10
|
from dlt_filesystem.util.python import asbool, cast_to_bool, cast_to_dict, cast_to_int
|
|
11
11
|
|
|
12
12
|
|
|
@@ -42,8 +42,9 @@ class WebdavSource(FilesystemSource):
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
# Decode individual arguments.
|
|
45
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
45
46
|
fs_kwargs = locator.options.fs_kwargs
|
|
46
|
-
fs_kwargs.update(
|
|
47
|
+
fs_kwargs.update(connector_kwargs)
|
|
47
48
|
cast_to_bool(fs_kwargs, ["retry", "trust_env"])
|
|
48
49
|
cast_to_dict(fs_kwargs, ["headers", "cookies", "proxies"])
|
|
49
50
|
cast_to_int(fs_kwargs, ["chunk_size", "max_redirects"])
|
|
@@ -68,4 +69,4 @@ class WebdavSource(FilesystemSource):
|
|
|
68
69
|
|
|
69
70
|
# Create filesystem and dlt resource wrapper.
|
|
70
71
|
fs = self.fs_class(uri, auth=auth, **fs_kwargs)
|
|
71
|
-
return infer_resource(fs=fs, locator=locator)
|
|
72
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -5,7 +5,7 @@ from fsspec import AbstractFileSystem
|
|
|
5
5
|
from dlt_filesystem.error import MissingConnectorOption
|
|
6
6
|
from dlt_filesystem.source.base import FilesystemSource
|
|
7
7
|
from dlt_filesystem.source.core import infer_resource
|
|
8
|
-
from dlt_filesystem.source.model import FilesystemLocator
|
|
8
|
+
from dlt_filesystem.source.model import FilesystemLocator, split_run_options
|
|
9
9
|
from dlt_filesystem.util.python import (
|
|
10
10
|
apply_alias,
|
|
11
11
|
cast_to_bool,
|
|
@@ -44,8 +44,9 @@ class WebHDFSSource(FilesystemSource):
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
# Decode individual options (type casting, default values, sanity checks).
|
|
47
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
47
48
|
fs_kwargs = locator.options.fs_kwargs
|
|
48
|
-
fs_kwargs.update(
|
|
49
|
+
fs_kwargs.update(connector_kwargs)
|
|
49
50
|
apply_alias(fs_kwargs, "kerberos_options", "kerb_kwargs")
|
|
50
51
|
cast_to_int(fs_kwargs, ["port"])
|
|
51
52
|
cast_to_bool(fs_kwargs, ["kerberos", "use_https", "session_verify"])
|
|
@@ -57,4 +58,4 @@ class WebHDFSSource(FilesystemSource):
|
|
|
57
58
|
|
|
58
59
|
# Create filesystem and dlt resource wrapper.
|
|
59
60
|
fs = self.fs_class(**fs_kwargs)
|
|
60
|
-
return infer_resource(fs=fs, locator=locator)
|
|
61
|
+
return infer_resource(fs=fs, locator=locator, options=resource_options)
|
|
@@ -8,6 +8,7 @@ from dlt_filesystem.error import InvalidBlobTableError, MissingConnectorOption
|
|
|
8
8
|
from dlt_filesystem.source.base import FilesystemSource
|
|
9
9
|
from dlt_filesystem.source.error import UnsupportedEndpointError
|
|
10
10
|
from dlt_filesystem.source.format.registry import supported_file_format_message
|
|
11
|
+
from dlt_filesystem.source.model import split_run_options, strip_run_options
|
|
11
12
|
from dlt_filesystem.source.router import (
|
|
12
13
|
blob_hints,
|
|
13
14
|
determine_endpoint,
|
|
@@ -55,7 +56,13 @@ class GCSSource(FilesystemSource):
|
|
|
55
56
|
|
|
56
57
|
bucket_url = f"gs://{bucket_name}"
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
# gcsfs takes the caller's own keyword arguments as the baseline it merges the
|
|
60
|
+
# URI parameters into, and this connector merges the query string wholesale, so
|
|
61
|
+
# both carriers get filtered before they reach the constructor.
|
|
62
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
63
|
+
fs = self.fs_class(
|
|
64
|
+
**gcs_filesystem_kwargs(strip_run_options(params), connector_kwargs)
|
|
65
|
+
)
|
|
59
66
|
|
|
60
67
|
try:
|
|
61
68
|
endpoint: str = determine_endpoint(table, path_to_file)
|
|
@@ -76,10 +83,10 @@ class GCSSource(FilesystemSource):
|
|
|
76
83
|
file_glob=path_to_file,
|
|
77
84
|
reader_name=endpoint,
|
|
78
85
|
storage_namespace="gcs",
|
|
79
|
-
filesystem_incremental=
|
|
86
|
+
filesystem_incremental=resource_options.filesystem_incremental,
|
|
80
87
|
require_file_match=source_selects_single_file(uri, table),
|
|
81
88
|
hints=blob_hints(parsed_uri, table),
|
|
82
|
-
column_types=
|
|
89
|
+
column_types=resource_options.column_types,
|
|
83
90
|
)
|
|
84
91
|
)
|
|
85
92
|
|
|
@@ -199,8 +206,9 @@ class AzureSource(FilesystemSource):
|
|
|
199
206
|
|
|
200
207
|
bucket_url = f"az://{bucket_name}"
|
|
201
208
|
|
|
202
|
-
kwargs
|
|
203
|
-
|
|
209
|
+
resource_options, connector_kwargs = split_run_options(kwargs)
|
|
210
|
+
connector_kwargs.update(azure_blob_filesystem_kwargs(auth))
|
|
211
|
+
fs = self.fs_class(**connector_kwargs)
|
|
204
212
|
|
|
205
213
|
try:
|
|
206
214
|
endpoint: str = determine_endpoint(table, path_to_file)
|
|
@@ -224,10 +232,10 @@ class AzureSource(FilesystemSource):
|
|
|
224
232
|
f"azure:{(auth.account_name or '').lower()}:"
|
|
225
233
|
f"{self.endpoint_namespace(auth.account_host, 'azure-public')}"
|
|
226
234
|
),
|
|
227
|
-
filesystem_incremental=
|
|
235
|
+
filesystem_incremental=resource_options.filesystem_incremental,
|
|
228
236
|
require_file_match=source_selects_single_file(uri, table),
|
|
229
237
|
hints=blob_hints(parsed_uri, table),
|
|
230
|
-
column_types=
|
|
238
|
+
column_types=resource_options.column_types,
|
|
231
239
|
)
|
|
232
240
|
)
|
|
233
241
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""File discovery for the filesystem sources.
|
|
2
|
+
|
|
3
|
+
``glob_files`` is vendored from dlt (Apache-2.0,
|
|
4
|
+
``dlt.common.storages.fsspec_filesystem``) so a file's modification date is
|
|
5
|
+
resolved from the shape the filesystem client actually returns.
|
|
6
|
+
|
|
7
|
+
dlt selects that value through a table keyed by URL scheme, where each entry
|
|
8
|
+
reads one backend-specific key out of the listing (``LastModified`` for ``s3``,
|
|
9
|
+
``updated`` for ``gs``, ``last_modified`` for ``az``). Both halves of that
|
|
10
|
+
coupling break here:
|
|
11
|
+
|
|
12
|
+
- A scheme dlt does not know raises ``KeyError`` on the scheme itself, so
|
|
13
|
+
listing crashes on the first matched file for ``r2://``, ``oss://``,
|
|
14
|
+
``hdfs://``, ``smb://``, ``ftp://``, ``dbfs://``, ``oci://`` and
|
|
15
|
+
``webhdfs://``.
|
|
16
|
+
- A client whose listing uses a different key than its scheme is mapped to
|
|
17
|
+
raises ``KeyError`` on the key, which is what any ``pyarrow.fs``-backed
|
|
18
|
+
client does (its entries carry ``mtime`` whatever the scheme).
|
|
19
|
+
|
|
20
|
+
Resolving from the listing keeps every scheme dlt does know byte-identical,
|
|
21
|
+
because its own table is consulted first.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import glob
|
|
25
|
+
import pathlib
|
|
26
|
+
import posixpath
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
from typing import Any, Callable, Iterator, Mapping, Tuple
|
|
29
|
+
from urllib.parse import urlparse
|
|
30
|
+
|
|
31
|
+
from dlt.common.storages import FilesystemConfiguration
|
|
32
|
+
from dlt.common.storages.configuration import make_fsspec_url
|
|
33
|
+
from dlt.common.storages.fsspec_filesystem import (
|
|
34
|
+
MTIME_DISPATCH,
|
|
35
|
+
FileItem,
|
|
36
|
+
guess_mime_type,
|
|
37
|
+
)
|
|
38
|
+
from dlt.common.time import ensure_pendulum_datetime_utc
|
|
39
|
+
from fsspec import AbstractFileSystem
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _from_epoch_millis(value: Any) -> Any:
|
|
43
|
+
"""Read a timestamp in milliseconds, as WebHDFS reports one."""
|
|
44
|
+
return ensure_pendulum_datetime_utc(float(value) / 1000)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _from_mlsd_timestamp(value: Any) -> Any:
|
|
48
|
+
"""Read an RFC 3659 ``modify`` fact, ``YYYYMMDDHHMMSS[.sss]``, as FTP reports one.
|
|
49
|
+
|
|
50
|
+
fsspec falls back to parsing ``dir`` output on servers without MLSD, which
|
|
51
|
+
yields a year-less ``Aug 4 09:30``. That cannot name an instant, so it is
|
|
52
|
+
rejected here and surfaces as the "no usable modification date" error.
|
|
53
|
+
"""
|
|
54
|
+
stamp = datetime.strptime(str(value)[:14], "%Y%m%d%H%M%S")
|
|
55
|
+
return ensure_pendulum_datetime_utc(stamp.replace(tzinfo=timezone.utc))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# The key each backend's listing carries a file's last-modified time under,
|
|
59
|
+
# paired with how that backend encodes it. Every entry is taken from the
|
|
60
|
+
# backend's own `modified()` implementation, which reads the same key.
|
|
61
|
+
MODIFICATION_DATE_KEYS: Tuple[Tuple[str, Callable[[Any], Any]], ...] = (
|
|
62
|
+
("LastModified", ensure_pendulum_datetime_utc), # s3fs, ossfs
|
|
63
|
+
("last_modified", ensure_pendulum_datetime_utc), # adlfs
|
|
64
|
+
("updated", ensure_pendulum_datetime_utc), # gcsfs
|
|
65
|
+
("modifiedTime", ensure_pendulum_datetime_utc), # Google Drive
|
|
66
|
+
("modificationTime", _from_epoch_millis), # WebHDFS
|
|
67
|
+
("timeModified", ensure_pendulum_datetime_utc), # ocifs
|
|
68
|
+
("modified", ensure_pendulum_datetime_utc), # fsspec-databricks
|
|
69
|
+
("modify", _from_mlsd_timestamp), # FTP
|
|
70
|
+
# `mtime` last: SMB carries both it and `time`, where `time` is the access
|
|
71
|
+
# time, so a backend that offers a more specific key is preferred first.
|
|
72
|
+
("mtime", ensure_pendulum_datetime_utc), # local, SMB, pyarrow.fs clients
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def resolve_modification_date(scheme: str, file_info: Mapping[str, Any]) -> Any:
|
|
77
|
+
"""Return a file's modification date from one entry of a filesystem listing.
|
|
78
|
+
|
|
79
|
+
Prefers dlt's own per-scheme extractor so known schemes keep their exact
|
|
80
|
+
behaviour, then reads the key the backend emits and decodes it the way that
|
|
81
|
+
backend encodes it.
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
ValueError: when the listing carries no usable modification date, naming
|
|
85
|
+
the scheme, the keys present, and why any candidate was rejected.
|
|
86
|
+
"""
|
|
87
|
+
extractor = MTIME_DISPATCH.get(scheme)
|
|
88
|
+
if extractor is not None:
|
|
89
|
+
try:
|
|
90
|
+
return extractor(file_info)
|
|
91
|
+
except KeyError:
|
|
92
|
+
# The scheme is known but this client reports a different key, e.g.
|
|
93
|
+
# any pyarrow.fs filesystem addressed as `s3://`.
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
rejected = []
|
|
97
|
+
for key, decode in MODIFICATION_DATE_KEYS:
|
|
98
|
+
if key not in file_info:
|
|
99
|
+
continue
|
|
100
|
+
try:
|
|
101
|
+
return decode(file_info[key])
|
|
102
|
+
except (ValueError, TypeError, OverflowError) as ex:
|
|
103
|
+
rejected.append(f"{key}={file_info[key]!r} ({ex})")
|
|
104
|
+
|
|
105
|
+
detail = f" Rejected: {'; '.join(rejected)}." if rejected else ""
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"Filesystem listing for scheme '{scheme}' carries no usable modification "
|
|
108
|
+
f"date. Keys present: {sorted(file_info)}.{detail}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def glob_files(
|
|
113
|
+
fs_client: AbstractFileSystem, bucket_url: str, file_glob: str = "**"
|
|
114
|
+
) -> Iterator[FileItem]:
|
|
115
|
+
"""Get the files from the filesystem client.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
fs_client (AbstractFileSystem): The filesystem client.
|
|
119
|
+
bucket_url (str): The url to the bucket.
|
|
120
|
+
file_glob (str): A glob for the filename filter.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Iterable[FileItem]: The list of files.
|
|
124
|
+
"""
|
|
125
|
+
is_local_fs = "file" in fs_client.protocol
|
|
126
|
+
if is_local_fs and FilesystemConfiguration.is_local_path(bucket_url):
|
|
127
|
+
bucket_url = FilesystemConfiguration.make_file_url(bucket_url)
|
|
128
|
+
bucket_url_parsed = urlparse(bucket_url)
|
|
129
|
+
|
|
130
|
+
if is_local_fs:
|
|
131
|
+
root_dir = FilesystemConfiguration.make_local_path(bucket_url)
|
|
132
|
+
# use a Python glob to get files
|
|
133
|
+
files = glob.glob(
|
|
134
|
+
str(pathlib.Path(root_dir).joinpath(file_glob)), recursive=True
|
|
135
|
+
)
|
|
136
|
+
glob_result = {file: fs_client.info(file) for file in files}
|
|
137
|
+
else:
|
|
138
|
+
# convert to fs_path
|
|
139
|
+
root_dir = fs_client._strip_protocol(bucket_url)
|
|
140
|
+
filter_url = posixpath.join(root_dir, file_glob)
|
|
141
|
+
# dlt's copy invalidates the listing cache for the `hf` protocol here.
|
|
142
|
+
# No HuggingFace scheme is registered, so that branch is left out.
|
|
143
|
+
glob_result = fs_client.glob(filter_url, detail=True)
|
|
144
|
+
if isinstance(glob_result, list):
|
|
145
|
+
raise NotImplementedError(
|
|
146
|
+
"Cannot request details when using fsspec.glob. For adlfs (Azure)"
|
|
147
|
+
" please use version 2023.9.0 or later"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
for file, md in glob_result.items():
|
|
151
|
+
if md["type"] != "file":
|
|
152
|
+
continue
|
|
153
|
+
scheme = bucket_url_parsed.scheme
|
|
154
|
+
|
|
155
|
+
# relative paths are always POSIX
|
|
156
|
+
if is_local_fs:
|
|
157
|
+
# use OS pathlib for local paths
|
|
158
|
+
loc_path = pathlib.Path(file)
|
|
159
|
+
file_name = loc_path.name
|
|
160
|
+
rel_path = loc_path.relative_to(root_dir).as_posix()
|
|
161
|
+
file_url = FilesystemConfiguration.make_file_url(file)
|
|
162
|
+
else:
|
|
163
|
+
file_name = posixpath.basename(file)
|
|
164
|
+
rel_path = posixpath.relpath(file, root_dir)
|
|
165
|
+
file_url = make_fsspec_url(scheme, file, bucket_url)
|
|
166
|
+
|
|
167
|
+
mime_type, encoding = guess_mime_type(rel_path)
|
|
168
|
+
file_item = FileItem(
|
|
169
|
+
file_name=file_name,
|
|
170
|
+
relative_path=rel_path,
|
|
171
|
+
file_url=file_url,
|
|
172
|
+
mime_type=mime_type,
|
|
173
|
+
modification_date=resolve_modification_date(scheme, md),
|
|
174
|
+
size_in_bytes=int(md["size"]),
|
|
175
|
+
)
|
|
176
|
+
if encoding is not None:
|
|
177
|
+
file_item["encoding"] = encoding
|
|
178
|
+
yield file_item
|