Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ To install: ```pip install s3dol```
[Documentation](https://i2mint.github.io/s3dol/)


## Heads-up: v1 is coming — run `s3dol.diagnose()` now

A major, behaviour-clarifying v1 is in progress
(design: [`misc/docs/architecture.md`](misc/docs/architecture.md) and the ADRs in
[`misc/docs/decisions/`](misc/docs/decisions/)). Today's `S3Store` will keep
working through a compatibility shim, but a few v0 behaviours were bugs
(e.g. an explicit `endpoint_url=` being silently dropped whenever
`AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` are exported) and v1 fixes them —
which can *move where your data goes*.

Before upgrading to v1, run the diagnosis with the same arguments you pass
`S3Store` today, in the environment you deploy in:

```python
import s3dol

s3dol.diagnose(bucket_name='my-bucket', endpoint_url='...', path='...')
```

It prints what resolves (endpoint, region, signing, credential *source* — never
a secret), where each value came from, and a **v0-vs-v1 divergence table**
telling you whether the upgrade changes anything for your call. It never
raises; a failing resolution is part of the report.


## Set up credentials

Recommended prerequisite to make getting started easier but not required.
Expand Down
11 changes: 10 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,16 @@
#: module not listed here is deselected when no endpoint is reachable, so a new
#: endpoint-using test can never silently fail the local gate. Widen only after
#: verifying a module's tests are genuinely endpoint-free.
_S3_FREE_TEST_STEMS = frozenset({"test_url_for"})
_S3_FREE_TEST_STEMS = frozenset(
{
"test_url_for",
# Layer A (v1) suites: tier-1 pure + moto-mocked tier-2 — no endpoint.
"test_errors",
"test_presets",
"test_connection",
"test_diagnose",
}
)


def _probe_host_port():
Expand Down
38 changes: 38 additions & 0 deletions s3dol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,41 @@

from s3dol.base import S3Dol
from s3dol.store import S3Store

# New (v1) layer-A names, loaded lazily (PEP 562) so `import s3dol` does not
# pay for what a script does not use. `s3dol.diagnose(...)` is the ADR-0007 §5
# step-0 migration safety mechanism — run it with your current S3Store kwargs
# to see what resolves, from where, and whether the upcoming v1 moves it.
_LAZY_ATTRIBUTES = {
"diagnose": ("s3dol._diagnose", "diagnose"),
"S3Connection": "s3dol.connection",
"resolve": "s3dol.connection",
"Resolution": "s3dol.connection",
"Preset": "s3dol.presets",
"Capabilities": "s3dol.presets",
"register_preset": ("s3dol.presets", "register"),
}


def __getattr__(name):
try:
target = _LAZY_ATTRIBUTES[name]
except KeyError:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
import importlib

if isinstance(target, tuple):
module_path, attribute = target
else:
module_path, attribute = target, name
value = getattr(importlib.import_module(module_path), attribute)
# Cache the resolved attribute. This ALSO deliberately overrides the
# submodule binding that `import s3dol.diagnose` just created on this
# package — without it, `s3dol.diagnose` would resolve to the module on
# every access after the first, shadowing the function.
globals()[name] = value
return value


def __dir__():
return sorted(set(globals()) | set(_LAZY_ATTRIBUTES))
Loading
Loading