sqlalchemyseed 2.1.0__tar.gz → 2.2.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.
- {sqlalchemyseed-2.1.0/src/sqlalchemyseed.egg-info → sqlalchemyseed-2.2.0}/PKG-INFO +35 -1
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/README.md +34 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/pyproject.toml +3 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/__init__.py +1 -1
- sqlalchemyseed-2.2.0/src/sqlalchemyseed/__main__.py +6 -0
- sqlalchemyseed-2.2.0/src/sqlalchemyseed/cli.py +169 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0/src/sqlalchemyseed.egg-info}/PKG-INFO +35 -1
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed.egg-info/SOURCES.txt +4 -0
- sqlalchemyseed-2.2.0/src/sqlalchemyseed.egg-info/entry_points.txt +2 -0
- sqlalchemyseed-2.2.0/tests/test_cli.py +133 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/LICENSE +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/setup.cfg +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/_future/__init__.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/_future/seeder.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/attribute.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/constants.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/dynamic_seeder.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/errors.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/json.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/loader.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/seeder.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/util.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed/validator.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed.egg-info/dependency_links.txt +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed.egg-info/requires.txt +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed.egg-info/top_level.txt +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/tests/test_json.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/tests/test_loader.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/tests/test_seeder.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/tests/test_temp_seeder.py +0 -0
- {sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/tests/test_validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemyseed
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: SQLAlchemy Seeder
|
|
5
5
|
Author-email: Jedy Matt Tabasco <hello@jedymatt.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -89,6 +89,40 @@ data.json
|
|
|
89
89
|
}
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
## Command-line usage
|
|
93
|
+
|
|
94
|
+
Seed a database directly from data files without writing Python:
|
|
95
|
+
|
|
96
|
+
```shell
|
|
97
|
+
sqlalchemyseed data.json --url sqlite:///app.db
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The command accepts one or more files and/or directories (a directory seeds
|
|
101
|
+
every `.json`/`.yaml`/`.yml` file inside it, in sorted order):
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
sqlalchemyseed seeds/ --url "$DATABASE_URL"
|
|
105
|
+
sqlalchemyseed a.json b.yaml --url sqlite:///app.db
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The database URL may be passed with `--url` or the `DATABASE_URL` environment
|
|
109
|
+
variable. Model paths in the data files (e.g. `models.Person`) are resolved
|
|
110
|
+
against the current working directory, so run the command from your project
|
|
111
|
+
root.
|
|
112
|
+
|
|
113
|
+
Options:
|
|
114
|
+
|
|
115
|
+
- `--dry-run` — seed inside a transaction, then roll back (validate without writing)
|
|
116
|
+
- `--seeder hybrid` — use `HybridSeeder` instead of the default `Seeder`
|
|
117
|
+
- `--model models.Person` — required for CSV inputs, which are not self-describing
|
|
118
|
+
- `--ref-prefix` — override the relationship reference prefix (default `!`)
|
|
119
|
+
|
|
120
|
+
The same command is available as a module:
|
|
121
|
+
|
|
122
|
+
```shell
|
|
123
|
+
python -m sqlalchemyseed data.json --url sqlite:///app.db
|
|
124
|
+
```
|
|
125
|
+
|
|
92
126
|
## Documentation
|
|
93
127
|
|
|
94
128
|
<https://sqlalchemyseed.readthedocs.io/>
|
|
@@ -64,6 +64,40 @@ data.json
|
|
|
64
64
|
}
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
## Command-line usage
|
|
68
|
+
|
|
69
|
+
Seed a database directly from data files without writing Python:
|
|
70
|
+
|
|
71
|
+
```shell
|
|
72
|
+
sqlalchemyseed data.json --url sqlite:///app.db
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The command accepts one or more files and/or directories (a directory seeds
|
|
76
|
+
every `.json`/`.yaml`/`.yml` file inside it, in sorted order):
|
|
77
|
+
|
|
78
|
+
```shell
|
|
79
|
+
sqlalchemyseed seeds/ --url "$DATABASE_URL"
|
|
80
|
+
sqlalchemyseed a.json b.yaml --url sqlite:///app.db
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The database URL may be passed with `--url` or the `DATABASE_URL` environment
|
|
84
|
+
variable. Model paths in the data files (e.g. `models.Person`) are resolved
|
|
85
|
+
against the current working directory, so run the command from your project
|
|
86
|
+
root.
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
|
|
90
|
+
- `--dry-run` — seed inside a transaction, then roll back (validate without writing)
|
|
91
|
+
- `--seeder hybrid` — use `HybridSeeder` instead of the default `Seeder`
|
|
92
|
+
- `--model models.Person` — required for CSV inputs, which are not self-describing
|
|
93
|
+
- `--ref-prefix` — override the relationship reference prefix (default `!`)
|
|
94
|
+
|
|
95
|
+
The same command is available as a module:
|
|
96
|
+
|
|
97
|
+
```shell
|
|
98
|
+
python -m sqlalchemyseed data.json --url sqlite:///app.db
|
|
99
|
+
```
|
|
100
|
+
|
|
67
101
|
## Documentation
|
|
68
102
|
|
|
69
103
|
<https://sqlalchemyseed.readthedocs.io/>
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Command-line interface for seeding a database from data files."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import sqlalchemy
|
|
9
|
+
from sqlalchemy.orm import Session
|
|
10
|
+
|
|
11
|
+
from . import loader
|
|
12
|
+
from .seeder import HybridSeeder, Seeder
|
|
13
|
+
|
|
14
|
+
_JSON_EXTENSIONS = {".json"}
|
|
15
|
+
_YAML_EXTENSIONS = {".yaml", ".yml"}
|
|
16
|
+
_CSV_EXTENSIONS = {".csv"}
|
|
17
|
+
# Only self-describing formats are auto-discovered inside a directory. CSV
|
|
18
|
+
# needs an explicit --model, so a CSV must be named as an individual file.
|
|
19
|
+
_DISCOVERABLE_EXTENSIONS = _JSON_EXTENSIONS | _YAML_EXTENSIONS
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
23
|
+
"""Build the argument parser for the ``sqlalchemyseed`` command."""
|
|
24
|
+
parser = argparse.ArgumentParser(
|
|
25
|
+
prog="sqlalchemyseed",
|
|
26
|
+
description="Seed a database from JSON, YAML, or CSV data files.",
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"paths",
|
|
30
|
+
nargs="+",
|
|
31
|
+
metavar="PATH",
|
|
32
|
+
help="data files or directories to seed from",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--url",
|
|
36
|
+
help="SQLAlchemy database URL (defaults to the DATABASE_URL env var)",
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"--seeder",
|
|
40
|
+
choices=("basic", "hybrid"),
|
|
41
|
+
default="basic",
|
|
42
|
+
help="seeder to use (default: basic)",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"--model",
|
|
46
|
+
help="model class path (e.g. models.Person) required for CSV inputs",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--ref-prefix",
|
|
50
|
+
default="!",
|
|
51
|
+
help="prefix marking relationship references (default: !)",
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--dry-run",
|
|
55
|
+
action="store_true",
|
|
56
|
+
help="seed within a transaction but roll back instead of committing",
|
|
57
|
+
)
|
|
58
|
+
return parser
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def collect_files(paths) -> list:
|
|
62
|
+
"""Expand each path into data files, walking directories in sorted order."""
|
|
63
|
+
files = []
|
|
64
|
+
for raw_path in paths:
|
|
65
|
+
files.extend(_files_in(Path(raw_path)))
|
|
66
|
+
return files
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _files_in(path: Path) -> list:
|
|
70
|
+
"""Return the data files contributed by a single path argument."""
|
|
71
|
+
if path.is_dir():
|
|
72
|
+
return _discover_directory(path)
|
|
73
|
+
if path.is_file():
|
|
74
|
+
return [path]
|
|
75
|
+
raise FileNotFoundError(f"path does not exist: {path}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _discover_directory(directory: Path) -> list:
|
|
79
|
+
"""Return the JSON/YAML files inside a directory, sorted by name."""
|
|
80
|
+
discovered = sorted(
|
|
81
|
+
child for child in directory.iterdir()
|
|
82
|
+
if child.suffix.lower() in _DISCOVERABLE_EXTENSIONS
|
|
83
|
+
)
|
|
84
|
+
if not discovered:
|
|
85
|
+
raise FileNotFoundError(
|
|
86
|
+
f"no JSON or YAML seed files found in directory: {directory}"
|
|
87
|
+
)
|
|
88
|
+
return discovered
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def load_file(path: Path, model=None) -> dict:
|
|
92
|
+
"""Load entities from a single data file, dispatching on its extension."""
|
|
93
|
+
suffix = path.suffix.lower()
|
|
94
|
+
if suffix in _JSON_EXTENSIONS:
|
|
95
|
+
return loader.load_entities_from_json(str(path))
|
|
96
|
+
if suffix in _YAML_EXTENSIONS:
|
|
97
|
+
return loader.load_entities_from_yaml(str(path))
|
|
98
|
+
if suffix in _CSV_EXTENSIONS:
|
|
99
|
+
return _load_csv(path, model)
|
|
100
|
+
raise ValueError(f"unsupported file type: {path}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _load_csv(path: Path, model) -> dict:
|
|
104
|
+
"""Load entities from a CSV file, which requires an explicit model."""
|
|
105
|
+
if model is None:
|
|
106
|
+
raise ValueError(f"CSV input requires --model to name the target class: {path}")
|
|
107
|
+
return loader.load_entities_from_csv(str(path), model)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _make_seeder(name, session, ref_prefix):
|
|
111
|
+
"""Return the seeder implementation selected on the command line."""
|
|
112
|
+
if name == "hybrid":
|
|
113
|
+
return HybridSeeder(session, ref_prefix=ref_prefix)
|
|
114
|
+
return Seeder(session, ref_prefix=ref_prefix)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _seed_all(seeder, files, model) -> int:
|
|
118
|
+
"""Seed every file through the seeder and return the entity count."""
|
|
119
|
+
seeded = 0
|
|
120
|
+
for path in files:
|
|
121
|
+
seeder.seed(load_file(path, model))
|
|
122
|
+
seeded += len(seeder.instances)
|
|
123
|
+
return seeded
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def main(argv=None) -> int:
|
|
127
|
+
"""Entry point for the ``sqlalchemyseed`` command."""
|
|
128
|
+
parser = build_parser()
|
|
129
|
+
args = parser.parse_args(argv)
|
|
130
|
+
|
|
131
|
+
url = args.url or os.environ.get("DATABASE_URL")
|
|
132
|
+
if not url:
|
|
133
|
+
parser.error("a database URL is required via --url or the DATABASE_URL env var")
|
|
134
|
+
|
|
135
|
+
# Make the caller's project importable so model paths like "models.Person"
|
|
136
|
+
# resolve against the current working directory.
|
|
137
|
+
sys.path.insert(0, os.getcwd())
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
files = collect_files(args.paths)
|
|
141
|
+
except FileNotFoundError as error:
|
|
142
|
+
parser.error(str(error))
|
|
143
|
+
|
|
144
|
+
engine = sqlalchemy.create_engine(url)
|
|
145
|
+
with Session(engine) as session:
|
|
146
|
+
seeder = _make_seeder(args.seeder, session, args.ref_prefix)
|
|
147
|
+
try:
|
|
148
|
+
seeded = _seed_all(seeder, files, args.model)
|
|
149
|
+
except Exception as error: # noqa: BLE001 - report any seeding failure as a clean exit
|
|
150
|
+
session.rollback()
|
|
151
|
+
print(f"error: {error}", file=sys.stderr)
|
|
152
|
+
return 1
|
|
153
|
+
|
|
154
|
+
return _finish(session, seeded, len(files), args.dry_run)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _finish(session, seeded, file_count, dry_run) -> int:
|
|
158
|
+
"""Commit or roll back the seeded session and print a summary."""
|
|
159
|
+
if dry_run:
|
|
160
|
+
session.rollback()
|
|
161
|
+
print(f"Dry run: would seed {seeded} entities from {file_count} file(s) (rolled back).")
|
|
162
|
+
return 0
|
|
163
|
+
session.commit()
|
|
164
|
+
print(f"Seeded {seeded} entities from {file_count} file(s).")
|
|
165
|
+
return 0
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__": # pragma: no cover
|
|
169
|
+
raise SystemExit(main())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemyseed
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: SQLAlchemy Seeder
|
|
5
5
|
Author-email: Jedy Matt Tabasco <hello@jedymatt.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -89,6 +89,40 @@ data.json
|
|
|
89
89
|
}
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
## Command-line usage
|
|
93
|
+
|
|
94
|
+
Seed a database directly from data files without writing Python:
|
|
95
|
+
|
|
96
|
+
```shell
|
|
97
|
+
sqlalchemyseed data.json --url sqlite:///app.db
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The command accepts one or more files and/or directories (a directory seeds
|
|
101
|
+
every `.json`/`.yaml`/`.yml` file inside it, in sorted order):
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
sqlalchemyseed seeds/ --url "$DATABASE_URL"
|
|
105
|
+
sqlalchemyseed a.json b.yaml --url sqlite:///app.db
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The database URL may be passed with `--url` or the `DATABASE_URL` environment
|
|
109
|
+
variable. Model paths in the data files (e.g. `models.Person`) are resolved
|
|
110
|
+
against the current working directory, so run the command from your project
|
|
111
|
+
root.
|
|
112
|
+
|
|
113
|
+
Options:
|
|
114
|
+
|
|
115
|
+
- `--dry-run` — seed inside a transaction, then roll back (validate without writing)
|
|
116
|
+
- `--seeder hybrid` — use `HybridSeeder` instead of the default `Seeder`
|
|
117
|
+
- `--model models.Person` — required for CSV inputs, which are not self-describing
|
|
118
|
+
- `--ref-prefix` — override the relationship reference prefix (default `!`)
|
|
119
|
+
|
|
120
|
+
The same command is available as a module:
|
|
121
|
+
|
|
122
|
+
```shell
|
|
123
|
+
python -m sqlalchemyseed data.json --url sqlite:///app.db
|
|
124
|
+
```
|
|
125
|
+
|
|
92
126
|
## Documentation
|
|
93
127
|
|
|
94
128
|
<https://sqlalchemyseed.readthedocs.io/>
|
|
@@ -2,7 +2,9 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
src/sqlalchemyseed/__init__.py
|
|
5
|
+
src/sqlalchemyseed/__main__.py
|
|
5
6
|
src/sqlalchemyseed/attribute.py
|
|
7
|
+
src/sqlalchemyseed/cli.py
|
|
6
8
|
src/sqlalchemyseed/constants.py
|
|
7
9
|
src/sqlalchemyseed/dynamic_seeder.py
|
|
8
10
|
src/sqlalchemyseed/errors.py
|
|
@@ -14,10 +16,12 @@ src/sqlalchemyseed/validator.py
|
|
|
14
16
|
src/sqlalchemyseed.egg-info/PKG-INFO
|
|
15
17
|
src/sqlalchemyseed.egg-info/SOURCES.txt
|
|
16
18
|
src/sqlalchemyseed.egg-info/dependency_links.txt
|
|
19
|
+
src/sqlalchemyseed.egg-info/entry_points.txt
|
|
17
20
|
src/sqlalchemyseed.egg-info/requires.txt
|
|
18
21
|
src/sqlalchemyseed.egg-info/top_level.txt
|
|
19
22
|
src/sqlalchemyseed/_future/__init__.py
|
|
20
23
|
src/sqlalchemyseed/_future/seeder.py
|
|
24
|
+
tests/test_cli.py
|
|
21
25
|
tests/test_json.py
|
|
22
26
|
tests/test_loader.py
|
|
23
27
|
tests/test_seeder.py
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Tests for the sqlalchemyseed command-line interface."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from sqlalchemy import create_engine
|
|
7
|
+
from sqlalchemy.orm import Session
|
|
8
|
+
|
|
9
|
+
from sqlalchemyseed import cli
|
|
10
|
+
from tests.models import Base, Person
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def db_url(tmp_path):
|
|
15
|
+
"""A file-backed SQLite URL with the test schema already created."""
|
|
16
|
+
url = f"sqlite:///{tmp_path / 'seed.db'}"
|
|
17
|
+
engine = create_engine(url)
|
|
18
|
+
Base.metadata.create_all(engine)
|
|
19
|
+
engine.dispose()
|
|
20
|
+
return url
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def count_persons(url):
|
|
24
|
+
with Session(create_engine(url)) as session:
|
|
25
|
+
return session.query(Person).count()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def write_json(path, entities):
|
|
29
|
+
path.write_text(json.dumps(entities), encoding="utf-8")
|
|
30
|
+
return path
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def person_entities(*names):
|
|
34
|
+
return {"model": "tests.models.Person", "data": [{"name": name} for name in names]}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_seed_json_file(tmp_path, db_url):
|
|
38
|
+
data_file = write_json(tmp_path / "people.json", person_entities("Alice", "Bob"))
|
|
39
|
+
|
|
40
|
+
assert cli.main([str(data_file), "--url", db_url]) == 0
|
|
41
|
+
assert count_persons(db_url) == 2
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_seed_yaml_file(tmp_path, db_url):
|
|
45
|
+
yaml_file = tmp_path / "people.yaml"
|
|
46
|
+
yaml_file.write_text(
|
|
47
|
+
"model: tests.models.Person\ndata:\n - name: Carol\n", encoding="utf-8"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
assert cli.main([str(yaml_file), "--url", db_url]) == 0
|
|
51
|
+
assert count_persons(db_url) == 1
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_seed_csv_file_requires_model(tmp_path, db_url):
|
|
55
|
+
csv_file = tmp_path / "people.csv"
|
|
56
|
+
csv_file.write_text("name\nDave\nErin\n", encoding="utf-8")
|
|
57
|
+
|
|
58
|
+
exit_code = cli.main(
|
|
59
|
+
[str(csv_file), "--url", db_url, "--model", "tests.models.Person"]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assert exit_code == 0
|
|
63
|
+
assert count_persons(db_url) == 2
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_csv_without_model_fails(tmp_path, db_url, capsys):
|
|
67
|
+
csv_file = tmp_path / "people.csv"
|
|
68
|
+
csv_file.write_text("name\nDave\n", encoding="utf-8")
|
|
69
|
+
|
|
70
|
+
assert cli.main([str(csv_file), "--url", db_url]) == 1
|
|
71
|
+
assert "requires --model" in capsys.readouterr().err
|
|
72
|
+
assert count_persons(db_url) == 0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_seed_directory(tmp_path, db_url):
|
|
76
|
+
seeds = tmp_path / "seeds"
|
|
77
|
+
seeds.mkdir()
|
|
78
|
+
write_json(seeds / "01_first.json", person_entities("Alice"))
|
|
79
|
+
write_json(seeds / "02_second.json", person_entities("Bob", "Carol"))
|
|
80
|
+
|
|
81
|
+
assert cli.main([str(seeds), "--url", db_url]) == 0
|
|
82
|
+
assert count_persons(db_url) == 3
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_multiple_paths(tmp_path, db_url):
|
|
86
|
+
first = write_json(tmp_path / "a.json", person_entities("Alice"))
|
|
87
|
+
second = write_json(tmp_path / "b.json", person_entities("Bob"))
|
|
88
|
+
|
|
89
|
+
assert cli.main([str(first), str(second), "--url", db_url]) == 0
|
|
90
|
+
assert count_persons(db_url) == 2
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_dry_run_rolls_back(tmp_path, db_url, capsys):
|
|
94
|
+
data_file = write_json(tmp_path / "people.json", person_entities("Alice", "Bob"))
|
|
95
|
+
|
|
96
|
+
assert cli.main([str(data_file), "--url", db_url, "--dry-run"]) == 0
|
|
97
|
+
assert "Dry run" in capsys.readouterr().out
|
|
98
|
+
assert count_persons(db_url) == 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_hybrid_seeder(tmp_path, db_url):
|
|
102
|
+
data_file = write_json(tmp_path / "people.json", person_entities("Alice"))
|
|
103
|
+
|
|
104
|
+
assert cli.main([str(data_file), "--url", db_url, "--seeder", "hybrid"]) == 0
|
|
105
|
+
assert count_persons(db_url) == 1
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_url_from_environment(tmp_path, db_url, monkeypatch):
|
|
109
|
+
monkeypatch.setenv("DATABASE_URL", db_url)
|
|
110
|
+
data_file = write_json(tmp_path / "people.json", person_entities("Alice"))
|
|
111
|
+
|
|
112
|
+
assert cli.main([str(data_file)]) == 0
|
|
113
|
+
assert count_persons(db_url) == 1
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_missing_url_errors(tmp_path, monkeypatch):
|
|
117
|
+
monkeypatch.delenv("DATABASE_URL", raising=False)
|
|
118
|
+
data_file = write_json(tmp_path / "people.json", person_entities("Alice"))
|
|
119
|
+
|
|
120
|
+
with pytest.raises(SystemExit):
|
|
121
|
+
cli.main([str(data_file)])
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_nonexistent_path_errors(db_url):
|
|
125
|
+
with pytest.raises(SystemExit):
|
|
126
|
+
cli.main(["does_not_exist.json", "--url", db_url])
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_unsupported_file_type(tmp_path, db_url):
|
|
130
|
+
bad_file = tmp_path / "people.txt"
|
|
131
|
+
bad_file.write_text("nope", encoding="utf-8")
|
|
132
|
+
|
|
133
|
+
assert cli.main([str(bad_file), "--url", db_url]) == 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlalchemyseed-2.1.0 → sqlalchemyseed-2.2.0}/src/sqlalchemyseed.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|