Core
Shared base classes and core abstractions: logging, string utilities, SQL safety.
Core package — lazy-loaded.
Contains logging, string utilities, SQL safety, and other core functions. All submodules load on first attribute access via PEP 562 __getattr__.
- siege_utilities.core.init_logger(name, log_to_file=False, log_dir='logs', level='INFO', max_bytes=5000000, backup_count=5, shared_log_file=None)[source]
Initialize a logger with specific configuration.
Deprecated since version The: name suggests this function does first-time setup distinct from
get_logger(), but both are get-or-create with shared config. Preferconfigure_shared_logging()to set the shared config andget_logger()to retrieve loggers; theinit_loggershape will be removed in a future release.- Parameters:
- Returns:
Configured logger instance
- Return type:
Configure shared logging for all loggers.
- siege_utilities.core.remove_wrapping_quotes_and_trim(target_string)[source]
Removes wrapping quotes (single or double) from a string and trims whitespace
- siege_utilities.core.clean_string(target_string)[source]
Clean a string by removing quotes, trimming whitespace, and normalizing.
- siege_utilities.core.normalize_whitespace(target_string)[source]
Normalize whitespace in a string by replacing multiple spaces with single space.
- Parameters:
target_string (str | None) – String to normalize
- Returns:
String with normalized whitespace
- Return type:
Examples
>>> normalize_whitespace("hello world") 'hello world' >>> normalize_whitespace("test\n\n\nvalue") 'test value'
- siege_utilities.core.snake_case(text)[source]
Convert text to snake_case.
Examples
>>> snake_case("HelloWorld") 'hello_world' >>> snake_case("hello-world") 'hello_world' >>> snake_case("Hello World") 'hello_world'
- siege_utilities.core.remove_non_alphanumeric(text, keep_chars='_-')[source]
Remove non-alphanumeric characters from text, optionally keeping specified chars.
- Parameters:
- Returns:
Cleaned text
- Return type:
Examples
>>> remove_non_alphanumeric("hello@world!") 'helloworld' >>> remove_non_alphanumeric("test_value-123", keep_chars="_-") 'test_value-123'
- siege_utilities.core.validate_sql_identifier(name, label='identifier', *, allow_dotted=False)[source]
Validate that name is a safe SQL identifier.
Checks that the identifier contains only alphanumeric characters and underscores, starts with a letter or underscore, and is non-empty. This prevents SQL injection when identifiers must be interpolated into SQL strings (e.g.,
CREATE DATABASE {name}).- Parameters:
name (str) – The identifier to validate (database name, table name, etc.).
label (str) – Human-readable label for error messages (e.g., “database”, “table”).
allow_dotted (bool) – If True, accept
schema.tableorcatalog.schema.table– every dot-separated component must itself be a safe identifier. Defaults to False to preserve backwards-compatible single-component behaviour.
- Returns:
The validated identifier (unchanged).
- Raises:
ValueError – If name is empty, structurally invalid, or (when
allow_dotted=False) contains a dot.- Return type:
Examples
>>> validate_sql_identifier("electronic_silver", "database") 'electronic_silver' >>> validate_sql_identifier("silver; DROP TABLE --", "database") Traceback (most recent call last): ... ValueError: Invalid SQL database: ... >>> validate_sql_identifier("public.users", "table", allow_dotted=True) 'public.users'
- siege_utilities.core.validate_sql_identifier_in(name, allowed, label='identifier')[source]
Validate that name is both structurally safe AND in allowed.
Use this when the caller has a known set of legal values – the typical case is a column name that must exist on a DataFrame:
validate_sql_identifier_in(geometry_col, df.columns, "column")
- siege_utilities.core.escape_sql_string_literal(value, *, dialect='standard')[source]
SQL-escape a string for use inside single-quoted SQL literals.
Use sparingly – parameter binding is always preferred. The cases that legitimately need this are:
Sedona / Spark-SQL spatial UDFs (
ST_GeomFromText) where Spark parameter substitution doesn’t apply uniformly across versions.Hand-written SQL files generated for human inspection / batch re-import – defensive escaping in case the row data is later hand-edited.
The escape rule is the SQL standard: replace
'with''. For MySQL (dialect="mysql"), backslashes are also doubled because MySQL treats\as an escape character by default (NO_BACKSLASH_ESCAPESoff).NUL bytes are rejected – most drivers refuse to send them anyway and they can split a query in C-string-aware backends.
- Parameters:
- Raises:
TypeError – value is not a string.
ValueError – value contains a NUL byte or dialect is unknown.
- Return type:
- siege_utilities.core.validate_sql_fragment(fragment, label='SQL fragment')[source]
Reject SQL fragments containing dangerous structural keywords.
This is defense-in-depth for cases where a free-form SQL expression (e.g. a WHERE clause) must be interpolated into a query string. Parameter binding is always preferred; use this only when binding is not possible.
The blocklist catches statement-level injection (semicolons, DDL/DML keywords, comments). It does NOT make arbitrary input safe — callers should still avoid passing untrusted user input.
- Parameters:
- Returns:
The validated fragment (unchanged).
- Raises:
TypeError – fragment is not a string.
ValueError – fragment contains a blocked pattern.
- Return type:
Examples
>>> validate_sql_fragment("state_fips = '06'") "state_fips = '06'" >>> validate_sql_fragment("1=1; DROP TABLE users--") Traceback (most recent call last): ... ValueError: Dangerous pattern in SQL fragment: ...
Submodules
Modern logging system for siege_utilities. Provides structured logging with proper configuration management.
- class siege_utilities.core.logging.LoggerManager[source]
Bases:
objectManages multiple loggers with consistent configuration.
Configure shared logging for all loggers.
- Parameters:
- Return type:
None
- class siege_utilities.core.logging.LoggingConfig[source]
Bases:
objectConfiguration for logging system.
- __init__(log_to_file=False, log_dir=<factory>, max_bytes=5000000, backup_count=5, log_to_console=True, console_level='INFO', file_level='DEBUG', shared_log_file=None, shared_level='INFO')
Configure shared logging for all loggers.
- siege_utilities.core.logging.init_logger(name, log_to_file=False, log_dir='logs', level='INFO', max_bytes=5000000, backup_count=5, shared_log_file=None)[source]
Initialize a logger with specific configuration.
Deprecated since version The: name suggests this function does first-time setup distinct from
get_logger(), but both are get-or-create with shared config. Preferconfigure_shared_logging()to set the shared config andget_logger()to retrieve loggers; theinit_loggershape will be removed in a future release.- Parameters:
- Returns:
Configured logger instance
- Return type:
- siege_utilities.core.logging.set_default_logger_name(name)[source]
Set the default logger name.
- Parameters:
name (str)
- Return type:
None
- siege_utilities.core.logging.log_critical(message, logger_name=None)[source]
Log a critical message.
- siege_utilities.core.logging.parse_log_level(level)[source]
Convert a string or numeric level into a logging level constant.
- siege_utilities.core.logging.temporary_logging_config(config)[source]
Temporarily change logging configuration.
- Parameters:
config (LoggingConfig)
- Return type:
Generator[None, None, None]
- siege_utilities.core.string_utils.remove_wrapping_quotes_and_trim(target_string)[source]
Removes wrapping quotes (single or double) from a string and trims whitespace
- siege_utilities.core.string_utils.clean_string(target_string)[source]
Clean a string by removing quotes, trimming whitespace, and normalizing.
- siege_utilities.core.string_utils.normalize_whitespace(target_string)[source]
Normalize whitespace in a string by replacing multiple spaces with single space.
- Parameters:
target_string (str | None) – String to normalize
- Returns:
String with normalized whitespace
- Return type:
Examples
>>> normalize_whitespace("hello world") 'hello world' >>> normalize_whitespace("test\n\n\nvalue") 'test value'
- siege_utilities.core.string_utils.snake_case(text)[source]
Convert text to snake_case.
Examples
>>> snake_case("HelloWorld") 'hello_world' >>> snake_case("hello-world") 'hello_world' >>> snake_case("Hello World") 'hello_world'
- siege_utilities.core.string_utils.remove_non_alphanumeric(text, keep_chars='_-')[source]
Remove non-alphanumeric characters from text, optionally keeping specified chars.
- Parameters:
- Returns:
Cleaned text
- Return type:
Examples
>>> remove_non_alphanumeric("hello@world!") 'helloworld' >>> remove_non_alphanumeric("test_value-123", keep_chars="_-") 'test_value-123'
SQL identifier validation + literal escaping for Hive/Spark/PostGIS/DuckDB/Snowflake.
Prevents SQL injection where identifiers (database/table/column names)
must be interpolated into SQL strings – most dialects don’t permit
parameter-bound identifiers, so we validate against a strict allow-list
instead. For value escaping, prefer parameter binding; this module’s
escape_sql_string_literal() is the fallback for cases where the
underlying API genuinely cannot bind (Sedona spatial UDFs, hand-written
SQL files for re-import, etc.).
Originally developed for pure-translation (electinfo/enterprise). Moved to siege_utilities as shared security-critical infrastructure.
- siege_utilities.core.sql_safety.validate_sql_identifier(name, label='identifier', *, allow_dotted=False)[source]
Validate that name is a safe SQL identifier.
Checks that the identifier contains only alphanumeric characters and underscores, starts with a letter or underscore, and is non-empty. This prevents SQL injection when identifiers must be interpolated into SQL strings (e.g.,
CREATE DATABASE {name}).- Parameters:
name (str) – The identifier to validate (database name, table name, etc.).
label (str) – Human-readable label for error messages (e.g., “database”, “table”).
allow_dotted (bool) – If True, accept
schema.tableorcatalog.schema.table– every dot-separated component must itself be a safe identifier. Defaults to False to preserve backwards-compatible single-component behaviour.
- Returns:
The validated identifier (unchanged).
- Raises:
ValueError – If name is empty, structurally invalid, or (when
allow_dotted=False) contains a dot.- Return type:
Examples
>>> validate_sql_identifier("electronic_silver", "database") 'electronic_silver' >>> validate_sql_identifier("silver; DROP TABLE --", "database") Traceback (most recent call last): ... ValueError: Invalid SQL database: ... >>> validate_sql_identifier("public.users", "table", allow_dotted=True) 'public.users'
- siege_utilities.core.sql_safety.validate_sql_identifier_in(name, allowed, label='identifier')[source]
Validate that name is both structurally safe AND in allowed.
Use this when the caller has a known set of legal values – the typical case is a column name that must exist on a DataFrame:
validate_sql_identifier_in(geometry_col, df.columns, "column")
- siege_utilities.core.sql_safety.escape_sql_string_literal(value, *, dialect='standard')[source]
SQL-escape a string for use inside single-quoted SQL literals.
Use sparingly – parameter binding is always preferred. The cases that legitimately need this are:
Sedona / Spark-SQL spatial UDFs (
ST_GeomFromText) where Spark parameter substitution doesn’t apply uniformly across versions.Hand-written SQL files generated for human inspection / batch re-import – defensive escaping in case the row data is later hand-edited.
The escape rule is the SQL standard: replace
'with''. For MySQL (dialect="mysql"), backslashes are also doubled because MySQL treats\as an escape character by default (NO_BACKSLASH_ESCAPESoff).NUL bytes are rejected – most drivers refuse to send them anyway and they can split a query in C-string-aware backends.
- Parameters:
- Raises:
TypeError – value is not a string.
ValueError – value contains a NUL byte or dialect is unknown.
- Return type:
- siege_utilities.core.sql_safety.validate_sql_fragment(fragment, label='SQL fragment')[source]
Reject SQL fragments containing dangerous structural keywords.
This is defense-in-depth for cases where a free-form SQL expression (e.g. a WHERE clause) must be interpolated into a query string. Parameter binding is always preferred; use this only when binding is not possible.
The blocklist catches statement-level injection (semicolons, DDL/DML keywords, comments). It does NOT make arbitrary input safe — callers should still avoid passing untrusted user input.
- Parameters:
- Returns:
The validated fragment (unchanged).
- Raises:
TypeError – fragment is not a string.
ValueError – fragment contains a blocked pattern.
- Return type:
Examples
>>> validate_sql_fragment("state_fips = '06'") "state_fips = '06'" >>> validate_sql_fragment("1=1; DROP TABLE users--") Traceback (most recent call last): ... ValueError: Dangerous pattern in SQL fragment: ...