Skip to content

Commit 173c430

Browse files
committed
Big cli refactor, improvements to mssql
1 parent c25193f commit 173c430

File tree

18 files changed

+615
-311
lines changed

18 files changed

+615
-311
lines changed

.github/workflows/pypi.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ jobs:
3636
# as different versions of mssql have different setup instructions https://learn.microsoft.com/en-us/sql/linux/quickstart-install-connect-ubuntu?view=sql-server-ver16
3737
runs-on: ubuntu-20.04
3838
env:
39+
MSSQL_PID: developer
3940
MSSQL_SA_PASSWORD: pASSw0rd
4041
PYNONYMIZER_DB_USER: sa
4142
PYNONYMIZER_DB_PASSWORD: pASSw0rd
@@ -45,7 +46,7 @@ jobs:
4546
with:
4647
python-version: '3.12'
4748
- name: Setup MSSQL
48-
run: bash tests_integration/mssql/install_mssql.sh
49+
run: bash tests_integration/install_mssql.sh
4950
- name: Setup test env
5051
run: python -m pip install . .[mssql] pytest
5152

CHANGELOG.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2020
- Better error messages for unsupported fake types - the error should now explain the problem and link to the docs in the right section. [#133]
2121
- Error message for when a fake_type is used with the wrong config kwargs (these would have previously been caught under "unsupported fake types")
2222
- event hooks for progress events. you can now use your own progress bar if you're invoking the process via the python interface.
23+
- support for connection strings in mssql.
24+
25+
### Changed
26+
- removed credentials validation (db_user, db_password) from the initial cli validation, as these are now handled by your original database. This should grant the most flexibility for different types of credentials.
2327

2428
### Removed
2529
- Positional INPUT. Use the -i/--input option instead
2630
- Positional STRATEGYFILE. Use the -s/--strategy option instead
2731
- Positional OUTPUT. Use the -o/--output option instead
2832
- Deprecated environment keys: `DB_TYPE, DB_HOST, DB_NAME, DB_USER, DB_PASS`.
2933
- `--fake-locale` `-l` `$PYNONYMIZER_FAKE_LOCALE` cli option. Use the `locale:` key in your strategyfile instead
30-
31-
### Changed
32-
- environment vars
34+
- `dotenv` is no longer a dependency. you are expected to load your environment variables yourself!
35+
- Removed local server check for mssql, as manual overrides using connection string will be possible. pynonymizer isn't going to stop you!
3336

3437
## [1.25.0] 2023-03-29
3538
### Changed

pynonymizer/cli.py

+61-36
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
import argparse
2-
from typing import Annotated, List, Optional, Union
3-
import dotenv
4-
import os
1+
from subprocess import CalledProcessError
2+
from typing import Annotated, List
53
import sys
64
import logging
75
import typer
6+
from pynonymizer.exceptions import DatabaseConnectionError
87
from pynonymizer.fake import UnsupportedFakeArgumentsError, UnsupportedFakeTypeError
8+
from pynonymizer.process_steps import StepActionMap
99
from pynonymizer.pynonymize import (
1010
ArgumentValidationError,
11-
DatabaseConnectionError,
1211
pynonymize,
1312
ProcessSteps,
1413
)
@@ -60,7 +59,7 @@ def main(
6059
typer.Option(
6160
"--start-at", help="Choose a step to begin the process (inclusive)."
6261
),
63-
] = None,
62+
] = "START",
6463
only_step: Annotated[str, typer.Option(help="Choose one step to perform.")] = None,
6564
skip_steps: Annotated[
6665
List[str],
@@ -74,7 +73,7 @@ def main(
7473
stop_at_step: Annotated[
7574
str,
7675
typer.Option("--stop-at", help="Choose a step to stop at (inclusive)."),
77-
] = None,
76+
] = "END",
7877
seed_rows: Annotated[
7978
int,
8079
typer.Option(
@@ -83,6 +82,14 @@ def main(
8382
help="Number of rows to populate the fake data table used during anonymization",
8483
),
8584
] = 150,
85+
mssql_connection_string: Annotated[
86+
str,
87+
typer.Option(
88+
"--mssql-connection-string",
89+
"-c",
90+
help="Pass additional options to the pyodbc connection. overrides existing connection arguments.",
91+
),
92+
] = None,
8693
mssql_driver: Annotated[
8794
str,
8895
typer.Option(
@@ -154,97 +161,115 @@ def main(
154161
155162
https://github.com/rwnx/pynonymizer
156163
"""
157-
logger = logging.getLogger()
158-
logger.setLevel(logging.DEBUG)
164+
root_logger = logging.getLogger()
165+
166+
loglevel = logging.INFO
167+
if verbose:
168+
loglevel = logging.DEBUG
159169

160-
logger.handlers.clear()
170+
root_logger.handlers.clear()
161171
console_handler = logging.StreamHandler(sys.stderr)
162-
console_handler.setLevel(logging.INFO)
172+
173+
console_handler.setLevel(loglevel)
163174
console_handler.setFormatter(logging.Formatter("%(message)s"))
164-
logger.addHandler(console_handler)
175+
root_logger.addHandler(console_handler)
176+
177+
# Default and Normalize args
178+
if only_step is not None:
179+
only_step = ProcessSteps.from_value(only_step)
165180

166-
# find the dotenv from the current working dir rather than the execution location
167-
dotenv_file = dotenv.find_dotenv(usecwd=True)
168-
dotenv.load_dotenv(dotenv_path=dotenv_file)
181+
start_at_step = ProcessSteps.from_value(start_at_step)
182+
stop_at_step = ProcessSteps.from_value(stop_at_step)
183+
184+
if skip_steps and len(skip_steps) > 0:
185+
skip_steps = [ProcessSteps.from_value(skip) for skip in skip_steps]
169186

170187
if verbose:
171188
console_handler.setLevel(logging.DEBUG)
172189

190+
actions = StepActionMap(
191+
start_at_step=start_at_step,
192+
stop_at_step=stop_at_step,
193+
skip_steps=skip_steps,
194+
dry_run=dry_run,
195+
only_step=only_step,
196+
)
197+
173198
# Add local project dir to path in case of custom provider imports
174199
if "." not in sys.path:
175200
sys.path.append(".")
176201
try:
177202
pynonymize(
203+
progress=tqdm,
204+
actions=actions,
178205
input_path=input,
179206
strategyfile_path=strategyfile,
180207
output_path=output,
181-
db_type=db_type,
182-
db_host=db_host,
183-
db_port=db_port,
184-
db_name=db_name,
185-
db_user=db_user,
186-
db_password=db_password,
187-
start_at_step=start_at_step,
188-
only_step=only_step,
189-
skip_steps=skip_steps,
190-
stop_at_step=stop_at_step,
191208
seed_rows=seed_rows,
192209
mssql_driver=mssql_driver,
193210
mssql_backup_compression=mssql_backup_compression,
194211
mysql_cmd_opts=mysql_cmd_opts,
195212
mysql_dump_opts=mysql_dump_opts,
196213
postgres_cmd_opts=postgres_cmd_opts,
197214
postgres_dump_opts=postgres_dump_opts,
198-
dry_run=dry_run,
199-
verbose=verbose,
200215
ignore_anonymization_errors=ignore_anonymization_errors,
201-
progress=tqdm,
216+
verbose=verbose,
217+
db_type=db_type,
218+
db_host=db_host,
219+
db_port=db_port,
220+
db_name=db_name,
221+
db_user=db_user,
222+
db_password=db_password,
223+
mssql_connection_string=mssql_connection_string,
202224
)
203225
except ModuleNotFoundError as error:
204226
if error.name == "pyodbc" and db_type == "mssql":
205-
logger.error("Missing Required Packages for database support.")
206-
logger.error("Install package extras: pip install pynonymizer[mssql]")
227+
root_logger.error("Missing Required Packages for database support.")
228+
root_logger.error("Install package extras: pip install pynonymizer[mssql]")
207229
sys.exit(1)
208230
else:
209231
raise error
210232
except ImportError as error:
211233
if error.name == "pyodbc" and db_type == "mssql":
212-
logger.error(
234+
root_logger.error(
213235
"Error importing pyodbc (mssql). "
214236
"The ODBC driver may not be installed on your system. See package `unixodbc`."
215237
)
216238
sys.exit(1)
217239
else:
218240
raise error
219241
except DatabaseConnectionError as error:
220-
logger.error("Failed to connect to database.")
242+
root_logger.error("Failed to connect to database.")
221243
if verbose:
222-
logger.error(error)
244+
root_logger.error(error)
223245
sys.exit(1)
224246
except ArgumentValidationError as error:
225-
logger.error(
247+
root_logger.error(
226248
"Missing values for required arguments: \n"
227249
+ "\n".join(error.validation_messages)
228250
+ "\nSet these using the command-line options or with environment variables. \n"
229251
"For a complete list, See the program help below.\n"
230252
)
231253
sys.exit(2)
232254
except UnsupportedFakeArgumentsError as error:
233-
logger.error(
255+
root_logger.error(
234256
f"There was an error while parsing the strategyfile. Unknown fake type: {error.fake_type} \n "
235257
+ f"This happens when additional kwargs are passed to a fake type that it doesnt support. \n"
236258
+ f"You can only configure generators using kwargs that Faker supports. \n"
237259
+ f"See https://github.com/rwnx/pynonymizer/blob/main/doc/strategyfiles.md#column-strategy-fake_update for usage information."
238260
)
239261
sys.exit(1)
240262
except UnsupportedFakeTypeError as error:
241-
logger.error(
263+
root_logger.error(
242264
f"There was an error while parsing the strategyfile. Unknown fake type: {error.fake_type} \n "
243265
+ f"This happens when an fake_update column strategy is used with a generator that doesn't exist. \n"
244266
+ f"You can only use data types that Faker supports. \n"
245267
+ f"See https://github.com/rwnx/pynonymizer/blob/main/doc/strategyfiles.md#column-strategy-fake_update for usage information."
246268
)
247269
sys.exit(1)
270+
except CalledProcessError as error:
271+
root_logger.error(error)
272+
sys.exit(1)
248273

249274

250275
def cli():

0 commit comments

Comments
 (0)