spark-sql-migrations 0.0.1__tar.gz → 0.0.3__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.
Files changed (23) hide show
  1. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/PKG-INFO +12 -4
  2. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/setup.py +17 -4
  3. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/__init__.py +2 -3
  4. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/spark_sql/spark_sql.py +23 -4
  5. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations.egg-info/PKG-INFO +12 -4
  6. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations.egg-info/SOURCES.txt +1 -1
  7. spark_sql_migrations-0.0.3/spark_sql_migrations.egg-info/entry_points.txt +2 -0
  8. spark_sql_migrations-0.0.1/spark_sql_migrations/custom_logging.py +0 -78
  9. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/LICENSE +0 -0
  10. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/MANIFEST.in +0 -0
  11. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/backend_shim.py +0 -0
  12. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/setup.cfg +0 -0
  13. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/migration_templates/default_migration_template.sql +0 -0
  14. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/migrations_initial/first_01_intial_create_cat_dbr_only.sql +0 -0
  15. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/migrations_initial/first_02_intial_create_schema_all.sql +0 -0
  16. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/migrations_initial/first_03_intial_create_state_table_all.sql +0 -0
  17. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/py.typed +0 -0
  18. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/spark_sql/__init__.py +0 -0
  19. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations/spark_utils.py +0 -0
  20. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations.egg-info/dependency_links.txt +0 -0
  21. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations.egg-info/namespace_packages.txt +0 -0
  22. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations.egg-info/requires.txt +0 -0
  23. {spark_sql_migrations-0.0.1 → spark_sql_migrations-0.0.3}/spark_sql_migrations.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spark_sql_migrations
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: Chained, idempotent SQL migrations for Spark and Databricks.
5
5
  Home-page: https://github.com/mcjug2015/spark_sql_migrations
6
6
  Author: Victor Semenov
@@ -103,9 +103,16 @@ Choose deliberately. SQL that only Databricks understands must not live in
103
103
  ## Running migrations
104
104
 
105
105
  ```bash
106
- python -m spark_sql_migrations.spark_sql.spark_sql run --cat spark_catalog --schema default --migrations-dir path/to/migrations
106
+ spark-sql-migrations run --cat spark_catalog --schema default --migrations-dir path/to/migrations
107
107
  ```
108
108
 
109
+ The command configures logging via `logging.basicConfig()`, so each applied migration is
110
+ logged to stderr at INFO. It leaves an already-configured root logger alone, and every
111
+ other entry point into this library inherits the host application's logging untouched.
112
+ The command needs a Spark flavour installed; it
113
+ fails to import on a bare install. `python -m spark_sql_migrations.spark_sql.spark_sql`
114
+ is equivalent.
115
+
109
116
  Or from Python, which is what a consuming project usually wraps:
110
117
 
111
118
  ```python
@@ -122,7 +129,8 @@ def get_migrations_dir():
122
129
  main(cat="spark_catalog", schema="default", migrations_dir=get_migrations_dir())
123
130
  ```
124
131
 
125
- `main` builds its own session via `get_spark()`. To supply your own — a session already
132
+ `main` builds its own session via `get_spark()` and, being library code, leaves logging to
133
+ you. To supply your own — a session already
126
134
  configured by your job, say — call `run_migrations(spark, cat, schema, output_folder,
127
135
  migrations_root)` directly.
128
136
 
@@ -134,7 +142,7 @@ before it is executed, so you can always read the exact SQL a run applied.
134
142
  Generate one rather than hand-rolling the header:
135
143
 
136
144
  ```bash
137
- python -m spark_sql_migrations.spark_sql.spark_sql create_new_migration --message "add batch id to metrics" --output-path path/to/migrations/all_spark_migrations
145
+ spark-sql-migrations create_new_migration --message "add batch id to metrics" --output-path path/to/migrations/all_spark_migrations
138
146
  ```
139
147
 
140
148
  That writes `<yymmdd>_<slug>_<revision_id>.sql` from the template shipped in the package
@@ -15,6 +15,11 @@ setup(**{
15
15
  'Typing :: Typed',
16
16
  ],
17
17
  'description': 'Chained, idempotent SQL migrations for Spark and Databricks.',
18
+ 'entry_points': {
19
+ 'console_scripts': [
20
+ 'spark-sql-migrations = spark_sql_migrations.spark_sql.spark_sql:cli',
21
+ ],
22
+ },
18
23
  'extras_require': {
19
24
  'local': [
20
25
  'pyspark[connect]>=4.0,<5',
@@ -99,12 +104,19 @@ Choose deliberately. SQL that only Databricks understands must not live in
99
104
  ## Running migrations
100
105
 
101
106
  ```bash
102
- python -m spark_sql_migrations.spark_sql.spark_sql run \
107
+ spark-sql-migrations run \
103
108
  --cat spark_catalog \
104
109
  --schema default \
105
110
  --migrations-dir path/to/migrations
106
111
  ```
107
112
 
113
+ The command configures logging via `logging.basicConfig()`, so each applied migration is
114
+ logged to stderr at INFO. It leaves an already-configured root logger alone, and every
115
+ other entry point into this library inherits the host application's logging untouched.
116
+ The command needs a Spark flavour installed; it
117
+ fails to import on a bare install. `python -m spark_sql_migrations.spark_sql.spark_sql`
118
+ is equivalent.
119
+
108
120
  Or from Python, which is what a consuming project usually wraps:
109
121
 
110
122
  ```python
@@ -121,7 +133,8 @@ def get_migrations_dir():
121
133
  main(cat="spark_catalog", schema="default", migrations_dir=get_migrations_dir())
122
134
  ```
123
135
 
124
- `main` builds its own session via `get_spark()`. To supply your own — a session already
136
+ `main` builds its own session via `get_spark()` and, being library code, leaves logging to
137
+ you. To supply your own — a session already
125
138
  configured by your job, say — call `run_migrations(spark, cat, schema, output_folder,
126
139
  migrations_root)` directly.
127
140
 
@@ -133,7 +146,7 @@ before it is executed, so you can always read the exact SQL a run applied.
133
146
  Generate one rather than hand-rolling the header:
134
147
 
135
148
  ```bash
136
- python -m spark_sql_migrations.spark_sql.spark_sql create_new_migration \
149
+ spark-sql-migrations create_new_migration \
137
150
  --message "add batch id to metrics" \
138
151
  --output-path path/to/migrations/all_spark_migrations
139
152
  ```
@@ -239,5 +252,5 @@ Apache License 2.0 — see [LICENSE](LICENSE).
239
252
  },
240
253
  'python_requires': '>=3.10',
241
254
  'url': 'https://github.com/mcjug2015/spark_sql_migrations',
242
- 'version': '0.0.1',
255
+ 'version': '0.0.3',
243
256
  })
@@ -2,7 +2,6 @@ import logging
2
2
 
3
3
  # A library must not configure logging when it is imported. A NullHandler on the
4
4
  # package root keeps our records off logging.lastResort while leaving every
5
- # decision about handlers, levels and formats to the host application. An
6
- # application that wants our opinionated setup calls
7
- # spark_sql_migrations.custom_logging.setup_logging() from its own entry point.
5
+ # decision about handlers, levels and formats to the host application, which
6
+ # configures logging from its own entry point.
8
7
  logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -260,7 +260,11 @@ def record_revision(spark, cat: str, schema: str, migration_type: str, revision_
260
260
 
261
261
 
262
262
  def migrate_w_rev(spark, output_folder, migrations_root, cat: str, schema: str, migration_type: str):
263
- """apply the migrations of one client chain that VERSION_TABLE hasn't recorded yet."""
263
+ """
264
+ apply the migrations of one client chain that VERSION_TABLE hasn't recorded yet.
265
+ TODO XXX permit applying up to a revision below the head revision
266
+ TODO XXX add backwards migrations, permit migrating backwards
267
+ """
264
268
  migrations_dir = get_migrations_dir(migrations_root, migration_type)
265
269
  unapplied = get_unapplied_migrations_list(spark, migrations_dir, migration_type, cat=cat, schema=schema)
266
270
 
@@ -325,9 +329,24 @@ def build_parser(): # pragma: no cover
325
329
  return parser
326
330
 
327
331
 
328
- if __name__ == "__main__": # pragma: no cover
329
- args = build_parser().parse_args()
330
- kwargs = vars(args)
332
+ def cli(argv=None): # pragma: no cover
333
+ """the `spark-sql-migrations` console command.
334
+
335
+ an application entry point, so unlike the rest of this package it configures
336
+ logging. basicConfig is a no-op once the root logger has handlers, which keeps
337
+ this from clobbering a host application that got there first.
338
+ """
339
+ logging.basicConfig(
340
+ level=logging.INFO,
341
+ format="%(levelname)s %(asctime)s %(name)s : %(message)s",
342
+ datefmt="%y/%m/%d %H:%M:%S",
343
+ )
344
+ logging.getLogger("py4j").setLevel(logging.ERROR)
345
+ kwargs = vars(build_parser().parse_args(argv))
331
346
  func = kwargs.pop("func")
332
347
  kwargs.pop("command")
333
348
  func(**kwargs)
349
+
350
+
351
+ if __name__ == "__main__": # pragma: no cover
352
+ cli()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spark_sql_migrations
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: Chained, idempotent SQL migrations for Spark and Databricks.
5
5
  Home-page: https://github.com/mcjug2015/spark_sql_migrations
6
6
  Author: Victor Semenov
@@ -103,9 +103,16 @@ Choose deliberately. SQL that only Databricks understands must not live in
103
103
  ## Running migrations
104
104
 
105
105
  ```bash
106
- python -m spark_sql_migrations.spark_sql.spark_sql run --cat spark_catalog --schema default --migrations-dir path/to/migrations
106
+ spark-sql-migrations run --cat spark_catalog --schema default --migrations-dir path/to/migrations
107
107
  ```
108
108
 
109
+ The command configures logging via `logging.basicConfig()`, so each applied migration is
110
+ logged to stderr at INFO. It leaves an already-configured root logger alone, and every
111
+ other entry point into this library inherits the host application's logging untouched.
112
+ The command needs a Spark flavour installed; it
113
+ fails to import on a bare install. `python -m spark_sql_migrations.spark_sql.spark_sql`
114
+ is equivalent.
115
+
109
116
  Or from Python, which is what a consuming project usually wraps:
110
117
 
111
118
  ```python
@@ -122,7 +129,8 @@ def get_migrations_dir():
122
129
  main(cat="spark_catalog", schema="default", migrations_dir=get_migrations_dir())
123
130
  ```
124
131
 
125
- `main` builds its own session via `get_spark()`. To supply your own — a session already
132
+ `main` builds its own session via `get_spark()` and, being library code, leaves logging to
133
+ you. To supply your own — a session already
126
134
  configured by your job, say — call `run_migrations(spark, cat, schema, output_folder,
127
135
  migrations_root)` directly.
128
136
 
@@ -134,7 +142,7 @@ before it is executed, so you can always read the exact SQL a run applied.
134
142
  Generate one rather than hand-rolling the header:
135
143
 
136
144
  ```bash
137
- python -m spark_sql_migrations.spark_sql.spark_sql create_new_migration --message "add batch id to metrics" --output-path path/to/migrations/all_spark_migrations
145
+ spark-sql-migrations create_new_migration --message "add batch id to metrics" --output-path path/to/migrations/all_spark_migrations
138
146
  ```
139
147
 
140
148
  That writes `<yymmdd>_<slug>_<revision_id>.sql` from the template shipped in the package
@@ -3,12 +3,12 @@ MANIFEST.in
3
3
  backend_shim.py
4
4
  setup.py
5
5
  spark_sql_migrations/__init__.py
6
- spark_sql_migrations/custom_logging.py
7
6
  spark_sql_migrations/py.typed
8
7
  spark_sql_migrations/spark_utils.py
9
8
  spark_sql_migrations.egg-info/PKG-INFO
10
9
  spark_sql_migrations.egg-info/SOURCES.txt
11
10
  spark_sql_migrations.egg-info/dependency_links.txt
11
+ spark_sql_migrations.egg-info/entry_points.txt
12
12
  spark_sql_migrations.egg-info/namespace_packages.txt
13
13
  spark_sql_migrations.egg-info/requires.txt
14
14
  spark_sql_migrations.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ spark-sql-migrations = spark_sql_migrations.spark_sql.spark_sql:cli
@@ -1,78 +0,0 @@
1
- """Opinionated logging configuration, for *applications* to opt into.
2
-
3
- Importing spark_sql_migrations never configures logging: library modules take a plain
4
- ``logging.getLogger(__name__)`` and inherit whatever the host application set
5
- up. An application that wants this format calls :func:`setup_logging` from its
6
- own entry point -- never at import time, and never from library code, because
7
- configuring the root logger is the application's decision to make.
8
- """
9
-
10
- import logging.config
11
- import os
12
-
13
- LOG_FILE_ENV_VAR = "SPARK_SQL_MIGRATIONS_LOG_FILE"
14
- DEFAULT_LOG_FILENAME = "local_log.log"
15
-
16
- FORMAT = "%(levelname)s %(asctime)s %(filename)s->%(funcName)s->%(lineno)d : %(message)s"
17
-
18
-
19
- def get_log_file_path():
20
- """where the file handler writes.
21
-
22
- resolved against the caller's cwd for the same reason as the spark warehouse
23
- dir: a __file__-relative default would land inside site-packages once this
24
- is installed from a wheel.
25
- """
26
- return os.environ.get(LOG_FILE_ENV_VAR, os.path.join(os.getcwd(), DEFAULT_LOG_FILENAME))
27
-
28
-
29
- def build_config():
30
- """built per call, not a module constant: get_log_file_path() reads the
31
- environment, and at module-import time the consumer hasn't set it yet."""
32
- return {
33
- "version": 1,
34
- "disable_existing_loggers": False,
35
- "formatters": {
36
- "simple": {
37
- "format": FORMAT,
38
- "datefmt": "%y/%m/%d %H:%M:%S",
39
- }
40
- },
41
- "handlers": {
42
- "stdout": {
43
- "class": "logging.StreamHandler",
44
- "level": "INFO",
45
- "formatter": "simple",
46
- "stream": "ext://sys.stdout",
47
- },
48
- "stderr": {
49
- "class": "logging.StreamHandler",
50
- "level": "ERROR",
51
- "formatter": "simple",
52
- "stream": "ext://sys.stderr",
53
- },
54
- "file": {
55
- "class": "logging.FileHandler",
56
- "formatter": "simple",
57
- "filename": get_log_file_path(),
58
- "mode": "a",
59
- },
60
- },
61
- "root": {"level": "DEBUG", "handlers": ["stderr", "stdout", "file"]},
62
- }
63
-
64
-
65
- def is_logging_configured():
66
- # Check if the root logger has any handlers explicitly assigned
67
- return len(logging.getLogger().handlers) >= 3
68
-
69
-
70
- def setup_logging():
71
- """configure the root logger with our handlers; safe to call more than once.
72
-
73
- for application entry points only -- see the module docstring.
74
- """
75
- if not is_logging_configured():
76
- logging.config.dictConfig(build_config())
77
- logging.getLogger("py4j").setLevel(logging.ERROR)
78
- return logging