Meerschaum 2¶
Historical release notes for the 2.x series.
2.9.x Releases¶
Read release notes
The 2.9 series added support for geometry data and improved the web console development experience.
v2.9.5¶
-
Add the
Query Datadropdown to pipes' cards. Similar to theRecent Datadropdown, you can now explore a pipe's data with theQuery Datadropdown. This supportsbegin,end,params, andlimitfiltering. -
Additional fixes for in-place syncs on PostGIS. Geometry columns are now correctly coalesced for PostGIS.
v2.9.2 – v2.9.4¶
-
Fix in-place syncs with
GEOMETRYcolumns. The query to retrieveGEOMETRYdata types for PostGIS has been fixed. -
Consolidate 1-minute chunks in
Pipe.get_chunk_bounds(). The chunk bounds returned fromPipe.get_chunk_bounds()(without specifiying anend) now consolidate the 1-minute chunk into the last chunk:
import meerschaum as mrsm
pipe = mrsm.Pipe('demo', 'chunk_bounds', columns={'datetime': 'dt'}, instance='sql:local')
pipe.sync([
{'dt': '2025-01-01'},
{'dt': '2025-01-02'},
])
mrsm.pprint(pipe.get_chunk_bounds(bounded=True))
# [
# (
# datetime.datetime(2025, 1, 1, 0, 0, tzinfo=datetime.timezone.utc),
# datetime.datetime(2025, 1, 2, 0, 1, tzinfo=datetime.timezone.utc)
# )
# ]
- Remove unnecessary
CREATE SCHEMA IF EXISTSchecks. - Fix unsupported syntax for Python 3.8.
- Truncate long connector keys in the Shell prompt.
- Fix null location keys behavior for pipes cards.
- Fix autoincrementing primary keys for custom schemas.
v2.9.0 – v2.9.1¶
- Add the dtype
geometry(andgeography). The new data typegeometryadds support for syncing GIS data (e.g. WKB/WKT, GeoJSON,shapelyobjects, andGeoDataFrames).
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'geometry',
instance='sql:local',
columns={
'primary': 'id',
},
dtypes={
'geom': 'geometry',
'id': 'int',
},
)
The geometry dtype syntax supports constraints for Geometry type and/or CRS (SRID):
Syncing a GeoDataFrame (without specifying an explicit dtype) will detect any CRS and geometry type:
import meerschaum as mrsm
import geopandas as gpd
path = 'path/to/shapefile.shp'
gdf = gpd.read_file(path)
pipe = mrsm.Pipe('demo', 'shapefile', instance='sql:local')
success, msg = pipe.sync(gdf)
print(pipe.dtypes['geometry'])
# geometry[Point, 6570]
-
Add the SQLConnector flavor
postgis. The new flavorpostgis(built atop thepostgresqlflavor) allows pipes to take natively supportGEOMETRY(andGEOGRAPHY) types. -
Add a pages sidebar to the Web Console. Clicking the Meerschaum logo on the Web Console will show the pages navigation sidebar, allowing you to easily expand the web app. Custom pages added via
@web_pageare grouped by plugin. You can override this behavior with thepage_groupparameter:
from meerschaum.plugins import web_page, dash_plugin
@dash_plugin
def init_dash(dash_app):
import dash.html as html
@web_page('users', page_group='Settings', login_required=True)
def my_new_settings_page():
return dbc.Container([
html.P("A new page `/dash/users/` added to the Settings page group.")
])
-
Add the property
instance_keystoapiconnectors. The optional propertyinstance_keysdetermines the value ofinstance_keysto be sent alongside pipe requests. -
Insert the Web Console navbar into custom pages. Custom pages added via
@web_pagewill now include the simple navbar by default, to more tightly integrate custom pages into the Web Console. This behavior may be disabled by settingskip_navbar=Truein@web_page:
# example.py
from meerschaum.plugins import web_page, dash_plugin
@dash_plugin
def init_app(dash_app):
import dash_bootstrap_components as dbc
import dash.html as html
@web_page('example-page', skip_navbar=True)
def example_page():
return dbc.Container(html.H1('Hello, world!'))
-
Create
INTcolumns for dtypesint32,SMALLINTforint16. TheSQLConnectornow maps the Pandas dtypesint32toINTandint16(andint8) toSMALLINTrather than defaulting toBIGINTfor everything. -
Fix serialization of
valkeypipes without indices. Pipes synced withoutcolumnsnow correctly serialize documents' keys. -
Add API endpoints for clearing pipes and chunk bounds. The endpoints
/pipes/{connector_keys}/{metric_key}/{location_key}/clearand/pipes/{connector_keys}/{metric_key}/{location_key}/chunk_boundsnow allow API users to clear pipes (rather than using the legacy actions endpoint) and get the values frompipe.get_chunk_bounds(). -
Skip venv locking on Windows.
- Shrink
fullDocker image size.
2.8.x Releases¶
Read release notes
The 2.8 series introduced batches to verify pipes as well as more granular control over exposed instances via the API.
v2.8.4¶
- Allow for pattern matching in
allowed_instance_keys. You may now generalize the instances exposed by the API by using Unix-style patterns in the listsystem:api:permissions:instances:allowed_instance_keys:
-
Return pipe attributes for the route
/pipes/{connector}/{metric}/{location}. The API routes/pipes/{connector}/{metric}/{location}and/pipes/{connector}/{metric}/{location}/attributesboth return pipe attributes. -
Check entire batches for
verify rowcounts. The commandverify rowcountswill now check batch boundaries before checking row-counts for individual chunks. This should moderately increase performance. -
Kill orphaned child processes when the parent job is killed. Jobs created with pipeline arguments should now kill associated child processes.
-
Add
--skip-hooks. The flag--skip-hooksprevents any sync hooks from firing when syncing pipes. -
Remove datetime rounding from
parse_schedule(). Scheduled actions now behave as expected, the current timestamp is no longer rounded to the nearest minute, which was causing issues with thestarting indelay feature. -
Fix
allowed_instance_keysenforcement.
v2.8.3¶
- Increase username limit to 60 characters.
- Add chunk retries to
Pipe.verify(). - Add instance keys to remaining pipes endpoints.
- Misc bugfixes.
v2.8.0 – v2.8.2¶
- Add batches to
Pipe.verify(). Verification syncs now run in sequential batches so that they may be interrupted and resumed. SeePipe.get_chunk_bounds_batches()for more information:
from datetime import timedelta
import meerschaum as mrsm
pipe = mrsm.Pipe('demo', 'get_chunk_bounds', instance='sql:local')
bounds = pipe.get_chunk_bounds(
chunk_interval=timedelta(hours=10),
begin='2025-01-10',
end='2025-01-15',
bounded=True,
)
batches = pipe.get_chunk_bounds_batches(bounds, workers=4)
mrsm.pprint(
[
tuple(
(str(bounds[0]), str(bounds[1]))
for bounds in batch
)
for batch in batches
]
)
# [
# (
# ('2025-01-10 00:00:00+00:00', '2025-01-10 10:00:00+00:00'),
# ('2025-01-10 10:00:00+00:00', '2025-01-10 20:00:00+00:00'),
# ('2025-01-10 20:00:00+00:00', '2025-01-11 06:00:00+00:00'),
# ('2025-01-11 06:00:00+00:00', '2025-01-11 16:00:00+00:00')
# ),
# (
# ('2025-01-11 16:00:00+00:00', '2025-01-12 02:00:00+00:00'),
# ('2025-01-12 02:00:00+00:00', '2025-01-12 12:00:00+00:00'),
# ('2025-01-12 12:00:00+00:00', '2025-01-12 22:00:00+00:00'),
# ('2025-01-12 22:00:00+00:00', '2025-01-13 08:00:00+00:00')
# ),
# (
# ('2025-01-13 08:00:00+00:00', '2025-01-13 18:00:00+00:00'),
# ('2025-01-13 18:00:00+00:00', '2025-01-14 04:00:00+00:00'),
# ('2025-01-14 04:00:00+00:00', '2025-01-14 14:00:00+00:00'),
# ('2025-01-14 14:00:00+00:00', '2025-01-15 00:00:00+00:00')
# )
# ]
-
Add
--skip-chunks-with-greater-rowcountstoverify pipes. The flag--skip-chunks-with-greater-rowcountswill compare a chunk's rowcount with the rowcount of the remote table and skip if the chunk is greater than or equal to the remote count. This is only applicable for connectors which implementremote=Truesupport forget_sync_time(). -
Add
verify rowcounts. The actionverify rowcounts(same as passing--check-rowcounts-onlytoverify pipes) will compare row-counts for a pipe's chunks against remote rowcounts. This is only applicable for connectors which implementget_pipe_rowcount()with support forremote=True. -
Add
remotetopipe.get_sync_time(). For pipes which support it (i.e. theSQLConnector), the optionremoteis intended to return the sync time of a pipe's fetch definition, like the optionremoteinPipe.get_rowcount(). -
Allow for the Web API to serve pipes from multiple instances. You can disable this behavior by setting
system:api:permissions:instances:allow_multiple_instancestofalse. You may also explicitly allow which instances may be accessed by the WebAPI by setting the listsystem:api:permissions:instances:allowed_instance_keys(defaults to["*"]). -
Fix memory leak for retrying failed chunks. Failed chunks were kept in memory and retried later. In resource-intensive syncs with large chunks and high failures, this would result in large objects not being freed and hogging memory. This situation has been fixed.
-
Add negation to job actions. Prefix a job name with an underscore to select all other jobs. This is useful for filtering out noise for
show logs. -
Add
Pipe.parent. As a quality-of-life improvement, the attributePipe.parentwill return the first member ofPipe.parents(if available). -
Use the current instance for new tabs in the Webterm. Clicking "New Tab" will open a new
tmuxwindow using the currently selected instance on the Web Console. -
Other webterm quality-of-life improvements. Added a size toggle button to allow for the webterm to take the entire page.
-
Additional refactoring work. The API endpoints code has been cleaned up.
-
Added system configurations. New options have been added to the
systemconfiguration, such asmax_response_row_limit,allow_multiple_instances,allowed_instance_keys.
2.7.x Releases¶
Read release notes
The 2.7 series greatly improved indexing, numerics support, added the bytes type, and allowed for bypassing dtype enforcement (Pipe.enforce) as well as introducing persistent Webterm sessions.
v2.7.9 – v2.7.10¶
-
Add persistent Webterm sessions. On the Web Console, the Webterm will attach to a persistent terminal for the current session's user.
-
Reconnect Webterms after client disconnect. If a Webterm socket connection is broken, the client logic will attempt to reconnect and attach to the
tmuxsession. -
Add
tmuxsessions to Webterms. Webterm sessions now connect totmuxsessions (tied to the user accounts). Setsystem:webterm:tmux:enabledtofalseto disabletmuxsessions. -
Limit concurrent connections during
verify pipes. To keep from exhausting the SQL connection pool, limit the number of concurrent intra-chunk connections. -
Return the precision and scale from a table's columns and types. Reading a table's columns and types with
meerschaum.utils.sql.get_table_columns_types()now returns the precision and scale forNUMERIC(DECIMAL) columns.
v2.7.8¶
- Add support for user-supplied precision and scale for
numericcolumns. You may now manually specify a numeric column's precision and scale:
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'numeric', 'precision_scale',
instance='sql:local',
dtypes={'val': 'numeric[5,2]'},
)
pipe.sync([{'val': '123.456'}])
print(pipe.get_data())
# val
# 0 123.46
-
Serialize
numericcolumns to exact values during bulk inserts. Decimal values are serialized when inserting intoNUMERICcolumns during bulk inserts. -
Return a generator when fetching with
SQLConnector. To alleviate memory pressure, skip loading the entire dataframe when fetching. -
Add
json_serialize_value()to handle custom dtypes. When serializing documents, passjson_serialize_valueas the default handler:
import json
from decimal import Decimal
from datetime import datetime, timezone
from meerschaum.utils.dtypes import json_serialize_value
print(json.dumps(
{
'bytes': b'hello, world!',
'decimal': Decimal('1.000000001'),
'datetime': datetime(2025, 1, 1, tzinfo=timezone.utc),
},
default=json_serialize_value,
indent=4,
))
# {
# "bytes": "aGVsbG8sIHdvcmxkIQ==",
# "decimal": "1.000000001",
# "datetime": "2025-01-01T00:00:00+00:00"
# }
- Fix an issue with the
WITHkeyword in pipe definitions for MSSQL. Previously, pipes with used with keywordWITHbut not as a CTE (e.g. to specify an index) were incorrectly parsed.
v2.7.7¶
- Add actions
drop indicesandindex pipes. You may now drop and create indices on pipes with the actionsdrop indicesandindex pipesor the pipe methodsdrop_indices()andcreate_indices():
import meerschaum as mrsm
pipe = mrsm.Pipe('demo', 'drop_indices', columns=['id'], instance='sql:local')
pipe.sync([{'id': 1}])
print(pipe.get_columns_indices())
# {'id': [{'name': 'IX_demo_drop_indices_id', 'type': 'INDEX'}]}
pipe.drop_indices()
print(pipe.get_columns_indices())
# {}
pipe.create_indices()
print(pipe.get_columns_indices())
# {'id': [{'name': 'IX_demo_drop_indices_id', 'type': 'INDEX'}]}
-
Remove
CAST()to datetime with selecting from a pipe's definition. For some databases, casting to the same dtype causes the query optimizer to ignore the datetime index. -
Add
INCLUDEclause to datetime index for MSSQL. This is to coax the query optimizer into using the datetime axis. -
Remove redundant unique index. The two competing unique indices have been combined into a single index (for the key
unique). The unique constraint (whenupsertis true) shares the name but has the prefixUQ_in place ofIX_. -
Add pipe parameter
null_indices. Set the pipe parameternull_indicestoFalsefor a performance improvement in situations where null index values are not expected. -
Apply backtrack minutes when fetching integer datetimes. Backtrack minutes are now applied to pipes with integer datetimes axes.
v2.7.6¶
-
Make temporary table names configurable. The values for temporary SQL tables may be set in
MRSM{system:connectors:sql:instance:temporary_target}. The new default prefix is'_', and the new default transaction length is 4. The values have been re-ordered to target, transaction ID, then label. -
Add connector completions to
copy pipes. When copying pipes, the connector keys prompt will offer auto-complete suggestions. -
Fix stale job results. When polling for job results, the job result is dropped from in-memory cache to avoid overwriting the on-disk result.
-
Format row counts and seconds into human-friendly text. Row counts and sync durations are now formatted into human-friendly representations.
-
Add digits to
generate_password(). Random strings frommeerschaum.utils.misc.generate_password()may now contain digits.
v2.7.3 – v2.7.5¶
- Allow for dynamic targets in SQL queries. Include a pipe definition in double curly braces (Ã la Jinja) to substitute a pipe's target into a templated query.
import meerschaum as mrsm
pipe = mrsm.Pipe('demo', 'template', target='foo', instance='sql:local')
_ = pipe.register()
downstream_pipe = mrsm.Pipe(
'sql:local', 'template',
instance='sql:local',
parameters={
'sql': "SELECT *\nFROM {{Pipe('demo', 'template', instance='sql:local')}}"
},
)
conn = mrsm.get_connector('sql:local')
print(conn.get_pipe_metadef(downstream_pipe))
# WITH "definition" AS (
# SELECT *
# FROM "foo"
# )
# SELECT *
# FROM "definition"
-
Add
--skip-enforce-dtypes. To override a pipe'senforceparameter, pass--skip-enforce-dtypesto a sync. -
Add bulk inserts for MSSQL. To disable this behavior, set
system:connectors:sql:bulk_insert:mssqltofalse. Bulk inserts for PostgreSQL-like flavors may now be disabled as well. -
Fix altering multiple column types for MSSQL. When a table has multiple columns to be altered, each column will have its own
ALTER TABLEquery. -
Skip enforcing custom dtypes when
enforce=False. To avoid confusion, special Meerschaum data types (numeric,json, etc.) are not coerced into objects whenenforce=False. -
Fix timezone-aware casts. A bug has been fixed where it was possible to mix timezone-aware and -naive casts in a single query. This patch ensures that this no longer occurs.
-
Explicitly cast timezone-aware datetimes as UTC in SQL syncs. By default, timezone-aware columns are now cast as time zone UTC in SQL. This may be skipped by setting
enforcetoFalse. -
Added virtual environment inter-process locks. Competing processes now cooperate for virtual environment verification, which protects installed packages.
v2.7.0 – v2.7.2¶
- Introduce the
bytesdata type. Instance connectors which support binary data (e.g.SQLConnector) may now take advantage of thebytesdtype. Other connectors (e.g.ValkeyConnector) may usemeerschaum.utils.dtypes.serialize_bytes()to store binary data as a base64-encoded string.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'bytes',
instance='sql:memory',
dtypes={'blob': 'bytes'},
)
pipe.sync([
{'blob': b'hello, world!'},
])
df = pipe.get_data()
binary_data = df['blob'][0]
print(binary_data.decode('utf-8'))
# hello, world!
from meerschaum.utils.dtypes import serialize_bytes, attempt_cast_to_bytes
df['encoded'] = df['blob'].apply(serialize_bytes)
df['decoded'] = df['encoded'].apply(attempt_cast_to_bytes)
print(df)
# blob encoded decoded
# 0 b'hello, world!' aGVsbG8sIHdvcmxkIQ== b'hello, world!'
- Allow for pipes to use the same column for
datetime,primary, andautoincrement=True. Pipes may now use the same column as thedatetimeaxis andprimarywithautoincrementset toTrue.
pipe = mrsm.Pipe(
'demo', 'datetime_primary_key', 'autoincrement',
instance='sql:local',
columns={
'datetime': 'Id',
'primary': 'Id',
},
autoincrement=True,
)
-
Only join on
primarywhen present. When the indexprimaryis set, use the column as the primary joining index. This will improve performance when syncing tables with a primary key. -
Add the parameter
enforce. The parameterenforce(defaultTrue) toggles data type enforcement behavior. WhenenforceisFalse, incoming data will not be cast to the desired data types. For static datasets where the incoming data is always expected to be of the correct dtypes, then it is recommended to setenforcetoFalseandstatictoTrue.
from decimal import Decimal
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'enforce',
instance='sql:memory',
enforce=False,
static=True,
autoincrement=True,
columns={
'primary': 'Id',
'datetime': 'Id',
},
dtypes={
'Id': 'int',
'Amount': 'numeric',
},
)
pipe.sync([
{'Amount': Decimal('1.11')},
{'Amount': Decimal('2.22')},
])
df = pipe.get_data()
print(df)
# Id Amount
# 0 1 1.11
# 1 2 2.22
-
Create the
datetimeaxis as a clustered index for MSSQL, even when aprimaryindex is specififed. Specifying adatetimeandprimaryindex will create a nonclusteredPRIMARY KEY. Specifying the same column as bothdatetimeandprimarywill create a clustered primary key (tip: this is useful whenautoincrement=True). -
Increase the default chunk interval to 43200 minutes. New hypertables will use a default chunksize of 30 days (43200 minutes).
-
Virtual environment bugfixes. Existing virtual environment packages are backed up before re-initializing a virtual environment. This fixes the issue of disappearing dependencies.
-
Store
numericasTEXTfor SQLite and DuckDB. Due to limited precision,numericcolumns are now stored asTEXT, then parsed intoDecimalobjects upon retrieval. -
Show the Webterm by default when changing instances. On the Web Console, changing the instance select will make the Webterm visible.
-
Improve dtype inference.
2.6.x Releases¶
Read release notes
The 2.6 series added the primary index, autoincrement, and migrated to timezone-aware datetimes by default, as well as many quality-of-life improvements, especially for MSSQL.
v2.6.17¶
- Add relative deltas to
starting inscheduler syntax. You may specify a delta in the job schedulerstartingsyntax:
-
Fix
drop pipesfor pipes on custom schemas. Pipes created under a specific schema are now correctly dropped. -
Enhance editing pipeline jobs. Pipeline jobs now provide the job label as the default text to be edited. Pipeline arguments are now placed on a separate line to improve legibility.
-
Disable the progress timer for jobs. The
sync pipesprogress timer will now be hidden when running through a job. -
Unset
MRSM_NOASKfor daemons. Now that jobs may accept user input, the environment variableMRSM_NOASKis no longer needed for jobs run as daemons (executorlocal). -
Replace
Cx_Oraclewithoracledb. The Oracle SQL driver is no longer required now that the default Python binding for Oracle isoracledb. -
Fix Oracle auto-incrementing for good. At long last, the mystery of Oracle auto-incrementing identity columns has been laid to rest.
v2.6.15 – v2.6.16¶
-
Fix inplace syncs without a
datetimeaxis. A bug introduced by a performance optimization has been fixed. Inplace pipes without adatetimeaxis will skip searching for date bounds. Settingupserttotruewill bypass this bug for previous releases. -
Skip invoking
get_sync_time()for pipes without adatetimeaxis. Invoking an instance connector'sget_sync_time()method will now only occur whendatetimeis set. -
Remove
guess_datetime()check fromSQLConnector.get_sync_time(). Because sync times are only checked for pipes with a dedicateddatetimecolumn, theguess_datetime()check has been removed from theSQLConnector.get_sync_time()method. -
Skip persisting default
targetto parameters. The default target table name will no longer be persisted toparameters. This helps avoid accidentally setting the wrong target table when copying pipes. -
Default to "no" for syncing data when copying pipes. The action
copy pipeswill no longer sync data by default, instead requiring an explicit yes to begin syncing. -
Fix the "Update query" button behavior on the Web Console. Existing but null keys are now accounted for when update a SQL pipe's query.
-
Fix another Oracle autoincrement edge case. Resetting the autoincrementing primary key value on Oracle will now behave as expected.
v2.6.10 – v2.6.14¶
-
Improve datetime timezone-awareness enforcement performance. Datetime columns are only parsed for timezone awareness if the desired awareness differs. This drastically speeds up sync times.
-
Switch to
tz_localize()when stripping timezone information. The previous method of using a lambda to replace individualtzinfoattributes did not scale well. Usingtz_localize()can be vectorized and greatly speeds up syncs, especially with large chunks. -
Add
enforce_dtypestoPipe.filter_existing(). You may optionally enforce dtype information duringfilter_existing(). This may be useful when implementing custom syncs for instance connectors. Note this may impact memory and compute performance.
import meerschaum as mrsm
import pandas as pd
pipe = mrsm.Pipe('a', 'b', instance='sql:local')
pipe.sync([{'a': 1}])
df = pd.DataFrame([{'a': '2'}])
### `enforce_dtypes=True` will suppress the differing dtypes warning.
unseen, update, delta = pipe.filter_existing(df, enforce_dtypes=True)
print(delta)
-
Fix
query_df()for null parameters. This is useful for when you may usequery_df()with onlyselect_columnsoromit_columns. -
Fix autoincrementing IDs for Oracle SQL.
-
Enforce security settings for creating jobs. Jobs and remote actions will only be accessible to admin users when running with
--secure(system:permissions:actions:non_adminin config).
v2.6.6 – v2.6.9¶
-
Improve metadata performance when syncing. Syncs via the SQLConnector now cache schema and index metadata, speeding up transactions.
-
Fix upserts for MySQL / MariaDB. Upserts in MySQL and MariaDB now use
ON DUPLICATEinstead ofREPLACE INTO. -
Fix dtype detection for index columns. A bug where new index columns were incorrectly created as
INThas been fixed. -
Delete old keys when dropping Valkey pipes. Dropping a pipe from Valkey now clears all old index keys.
-
Fix timezone-aware enforcement bugs.
v2.6.1 – v2.6.5¶
- Add
Pipe.tzinfo. Check if a pipe is timezone-aware withtzinfo:
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'timezone', 'aware',
columns={'datetime': 'dt'},
)
print(pipe.tzinfo)
# UTC
pipe = mrsm.Pipe(
'demo', 'timezone', 'naive',
columns={'datetime': 'dt'},
dtypes={'dt': 'datetime64[ns]'},
)
print(pipe.tzinfo)
# None
- Improve timezone enforcement when syncing.
- Fix inplace syncs with
upsert=True. - Fix timezone-aware datetime truncation for MSSQL
- Fix timezone detection for existing timezone-naive tables.
v2.6.0¶
-
Enforce a timezone-aware
datetimeaxis by default. Pipes now enforce timezone-naive datetimes as UTC, even if the underlying column type is timezone-naive. To use datetime-naive datetime axes, you must explicitly set thedtypetodatetime64[ns]. -
Designate the index name
primaryfor primary keys. Like thedatetimeindex, theprimaryindex is used for joins and will be created as the primary key in new tables.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'primary_key',
columns={'primary': 'pk'},
instance='sql:local',
)
### Raises a `UNIQUE` constraint failure:
pipe.sync([
{'pk': 1},
{'pk': 1},
])
- Add
autoincrementtoPipe.parametersLikeupsert, you may designate an incremental integer primary key by settingautoincrementtoTruein the pipe parameters. Note thatautoincrementwill beTrueif you specify aprimaryindex but do not specify a dtype or pass into the initial dataframe. This is only available forsqlpipes.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'autoincrement',
columns={'primary': 'id'},
autoincrement=True,
instance='sql:local',
)
pipe.sync([
{'color': 'red'},
{'color': 'blue'},
])
print(pipe.get_data())
# id color
# 0 1 red
# 1 2 blue
pipe.sync([
{'color': 'green', 'id': 1},
])
print(pipe.get_data())
# id color
# 0 1 green
# 1 2 blue
- Add option
statictoPipe.parametersto disable schema modification. SetstatictoTruein a pipe's parameters to prevent any modification of the column's data types.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'static',
columns={
'primary': 'id',
'datetime': 'id',
},
dtypes={
'meta': 'json',
'id': 'int',
},
parameters={
'static': True,
'upsert': True,
'autoincrement': True,
},
)
pipe.sync([{'id': 1, 'meta': {'foo': 'bar'}}])
### Abort syncing new columns when `static` is set.
pipe.sync([{'id': 1, 'color': 'blue'}])
mrsm.pprint(pipe.get_columns_types())
# {'id': 'BIGINT', 'meta': 'JSONB'}
- Add
get_create_table_queries()to build fromdtypesdictionaries. You may get aCREATE TABLEquery from adtypesdictionary (in addition to aSELECTquery). The functionmeerschaum.utils.sql.get_create_table_query()now also accepts an argumentsprimary_keyandautoincrementto designate a primary key column.
from meerschaum.utils.sql import get_create_table_queries
queries = get_create_table_queries(
{
"id": "int",
"is_active": "bool",
"customer_id": "uuid",
"metadata": "json",
},
'new_table',
'postgresql',
primary_key="id",
autoincrement=True,
)
print(queries[0])
# CREATE TABLE "new_table" (
# "id" BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
# "is_active" BOOLEAN,
# "customer_id" UUID,
# "metadata" JSONB
# )
-
Create a multi-column index for columns defined in
Pipe.columns. To disable this behavior, setuniquetoNoneinPipe.indices. -
Default to
BITfor boolean columns in MSSQL. The previous workaround was to storeboolcolumns asINT. This change now defaults toBITwhen creating new tables. Boolean columns cannot be nullable for MSSQL. -
Improve file protection in
edit config. Writing an invalid config file will now stop you before committing the changes. The previous behavior would lead to data loss. -
Catch exceptions when creating chunk labels. If a datetime bound cannot be determined for a chunk, return
pd.NA.
2.5.x Releases¶
Read release notes
The 2.5.x series was short and sweet, primarily introducing features relating to Pipe.indices.
v2.5.1¶
-
Update index information in the pipe card. The
Indicessection of the pipe card on the web console includes more detailed information, such as composite and multi-column indices. -
Print action results during scheduled jobs. Scheduled actions now print their result success tuples after firing.
-
Other bugfixes. A few bugs from the migration of
APSchedulerto internal management have been fixed.
v2.5.0¶
- Add
indicestoPipe.parameters. You may now explicitly state the indices to be created by definingindices(orindexes) inPipe.parameters(or thePipeconstructor for your convenience).
import meerschaum as mrsm
pipe = mrsm.Pipe(
'software', 'versions',
instance='sql:local',
columns=['major', 'minor', 'patch'],
indices={
'all': ['major', 'minor', 'patch'],
},
)
mrsm.pprint(pipe.get_indices())
# {
# 'major': 'IX_software_versions_major',
# 'minor': 'IX_software_versions_minor',
# 'patch': 'IX_software_versions_patch',
# 'all': 'IX_software_versions_major_minor_patch'
# }
You may also use the key index_template to change the format of the generated index names (defaults to IX_{target}_{column_names}, where target is the table name and column_names consists of all of the index's columns joined by an underscore).
pipe.parameters['index_template'] = "foo_{target}_{column_names}"
mrsm.pprint(pipe.get_indices())
# {'all': 'foo_software_versions_major_minor_patch'}
-
Enable chunking for MSSQL To improve memory usage,
chunksizeis now accepted bySQLConnector.read()for the flavormssql. -
Disable
pyodbcpooling. To properly recycle the connection pool in the SQLAlchemy engine, the internalpyodbcpooling must be disabled. See the SQLAlchemy documentation. -
Bugfixes Other miscellaneous bugfixes have been included in this release, such as resolving broken imports during certain edge cases.
2.4.x Releases¶
Read release notes
The 2.4.x series added the ValkeyConnector, relative --begin and --end, pipeline timeouts, and improved MSSQL support.
v2.4.13¶
- Add
--timeoutto pipeline arguments. You may now designate the maximum number of seconds to run a pipeline with--timeout. This will run the entire pipeline in a subprocess rather than a persistent session.
-
Add auto-complete to
edit jobsandbootstrap jobs. -
Improve the editing experience for
edit jobsandbootstrap jobs. -
Fixed plugin detection for Python 3.9.
v2.4.12¶
-
Add the actions
edit jobsandbootstrap jobs. The actionedit jobslets you easily tweak the arguments for an existing job, so there's no need to delete and recreate jobs. Thebootstrap jobswizard also gives you a chance to review your changes before starting a job. -
Fix nested CTEs for MSSQL. Pipes may now use definitions containing a
WITHclause for Microsoft SQL Server. -
Added
wrap_query_with_ctetomeerschaum.utils.sql. Reference a subquery in an encapsulating parent query, even if the subquery contains CTEs itself.
from meerschaum.utils.sql import wrap_query_with_cte
sub_query = """
WITH [foo] AS (
SELECT 1 AS [val]
)
SELECT ([val] * 2) AS [newval]
FROM [foo]
"""
parent_query = "SELECT (newval * 3) FROM [src]"
query = wrap_query_with_cte(
sub_query,
parent_query,
'mssql',
cte_name='src',
)
print(query)
# WITH [foo] AS (
# SELECT 1 AS [val]
# ),
# [src] AS (
# SELECT ([val] * 2) AS [newval]
# FROM [foo]
#
# )
# SELECT (newval * 3) FROM [src]
-
Fix
--yeswhen running in background jobs. The flags--yesand--noaskare now properly handled when running a background job which contains prompts. -
Add an external page for jobs to the Web Console. Like the shareable
/pipes/links, you may now link to a specific job at the path/dash/job/{name}. Click the name of the job on the card to open a job in a new tab. -
Preserve the original values for
--beginand--end. When creating jobs in the shell, the original string values for--beginand--endwill be preserved, such as in the case of--begin 1 month ago. -
Fix
Pipeformatting for small terminals. Pipes with long names are now properly rendered in small terminal windows. -
Enable shell suggestions for chained actions. The shell auto-complete now works with chained actions.
v2.4.9 – v2.4.11¶
- Add relative formats to
--beginand--end. The flags--beginand--endsupport values in the format[N] [unit] ago:
Add a second delta format (recommended to be denoted by the keyword rounded) to round the timestamp to a clean value:
Supported units are seconds, minutes, hours, days, weeks, months (ago only), and years.
-
Respect
--begin,--end, and--paramsinshow rowcounts. The flags--begin,--end, and--paramsare now handled in the actionshow rowcounts. -
Fix an issue with
Pipe.get_backtrack_data(). An incorrect calculation was fixed the produce the correct backtrack interval.
v2.4.8¶
-
Allow for syncing against
DATETIMEOFFSETcolumns in MSSQL. When syncing an existing table with aDATETIMEOFFSETcolumn, timestamps correctly coerced into timezone-naive UTC timestamps. Note this behavior will likely change to timezone-aware-by-default in a future release. -
Default to
DATETIME2for MSSQL. To preserve precision, MSSQL now creates datetime columns asDATETIME2. -
Remove temporary tables warnings. Failure to drop temporary tables no longer raises a warning (added
IF EXISTScheck). -
Fix an issue with the
sqlaction. -
Fix UUID support for SQLite, MySQL / MariaDB.
-
Set
IS_THREAD_SAFEtoFalsefor Oracle.
v2.4.6 – v2.4.7¶
-
Prefix temporary tables with
##. Temporary tables are now prefixed with##to take advantage oftempdbin MSSQL. -
Add the
uuiddtype. Theuuiddtype adds support for PythonUUIDobjects and maps to the appropriateUUIDdata type per SQL flavor (e.g.UNIQUEIDENTIFIERformssql). -
Add
upsertsupport to MSSQL. Settingupsertin a pipe's parameters will now upsert rows in a single transaction (via aMERGEquery). -
Add
SQLConnector.get_connection(). To simplify connection management, you may now obtain an active connection withSQLConnector.get_connection(). To force a new connection, passrebuild=True. -
Improve session management for MSSQL. Transactions and connections are now more gracefully handled when working with MSSQL.
v2.4.2 – v2.4.5¶
-
Fix
bootstrap connectors. Revert a breaking change to thebootstrap connectorswizard. -
Respect disabling
uvfor package installation. Settingsystem:experimental:uv_piptofalsewill now disableuvfor certain. -
Default to a query string for
optionswhen bootstrapping MSSQL connectors. Although dictionaries are supported foroptions, using a dictionary as a default was breaking serialization. The default foroptionsis now the stringdriver=ODBC Driver 17 for SQL Server&UseFMTONLY=Yes. -
Default to MSSQL ODBC Driver 18. The default driver to be used by MSSQL connectors is version 18.
v2.4.1¶
-
Add
instanceto the external pipe links. When sharing pipe links on the Web Console, the instance will now be included in the URL. -
Fix an issue with remote actions. An import error has been patched.
v2.4.0¶
-
Add
valkeyinstance connectors. Introducing a new first-class instance connector: theValkeyConnector. Valkey, a fork of Redis, is a high-performance in-memory database often used for caching. Thevalkeyservice has been added to the Meerschaum stack and is accessible via the built-in connectorvalkey:main. -
Cache Web Console sessions in Valkey when running with
--production. Starting the web API with--productionwill now store sessions invalkey:main. This results in a smoother experience in the event of a web server restart. By default, sessions expire after 30 days.You may disable this behavior by setting
system:experimental:valkey_session_cachetofalse. -
Allow for a default executor. Setting the key
meerschaum:executorwill set the default executor (overriding the check forsystemd). This is useful for defaulting to remote actions in a multi-node deployment. -
Allow querying for
Noneinquery_df(). You may now query for null rows:
import pandas as pd
from meerschaum.utils.dataframe import query_df
df = pd.DataFrame({'a': [1, 2, pd.NA]}).astype('Int64')
result = query_df(df, {'a': None})
print(result)
# a
# 2 <NA>
result = query_df(df, {'a': [None, 1]})
print(result)
# a
# 0 1
# 2 <NA>
-
Improve
query_df()performance. Dataframe vlues are no longer serialized by default inquery_df(), meaning that parameters must match the data type. Passcoerce_types=Trueto restore legacy behavior. -
Add
Pipe.copy_to(). Copy pipes between instances withPipe.copy_to():
- Add
include_unchanged_columnstoPipe.filter_existing(). Passinclude_unchanged_columns=Trueto return entire documents in the update dataframe. This is useful for situations where you are unable to update individual fields:
import meerschaum as mrsm
import pandas as pd
pipe = mrsm.Pipe('a', 'b', columns=['id'])
pipe.sync([
{'animal': 'cat', 'name': 'Meowth', 'id': 1},
{'animal': 'dog', 'name': 'Fluffy', 'id': 2},
])
df = pd.DataFrame([{'id': 1, 'breed': 'tabby'}])
unseen, update, delta = pipe.filter_existing(df)
print(update)
# id breed
# 0 1 tabby
unseen, update, delta = pipe.filter_existing(
df,
include_unchanged_columns=True,
)
print(update)
# animal name id breed
# 0 cat Meowth 1 tabby
-
Add a share button to the Pipe card. On the web dashboard, you may now more easily share pipes by clicking the "share" icon and copying the URL. This opens the pipe card in a new, dedicated tab.
-
Add
OPTIONAL_ATTRIBUTESto connectors. Connectors may now setOPTIONAL_ATTRIBUTES, which will add skippable prompts inbootstrap connector. -
Remove progress bar for syncing via remote actions. Executing
sync pipesremotely will no longer print the timer progress bar. -
Fix bug with
stackin the shell. Note thatstackactions may not be chained. -
Fix scheduler dependency. To fix the installation of
APScheduler,attrsis now held back to 24.1.0.
2.3.x Releases¶
Read release notes
The 2.3 series was short but brought significant improvements, notably the Job API, remote jobs, and action chaining.
v2.3.5 – v2.3.6¶
-
Properly handle remote jobs. Long-running remote jobs are now properly handled, allowing for graceful API shutdown.
-
Detect when creating a remote pipeline. Running a pipeline action with a remote executor will pass through the pipeline to the API server:
-
Remove actions websocket endpoint with temporary jobs.
-
Properly quote environment variables in
systemdservices. -
Remove
~/.localvolume fromapiservice in the stack. This was overwriting the new Docker image version and such needed to be removed.
v2.3.0 – v2.3.4¶
- Add the
Jobclass. You may now manage jobs withJob:
If you are running on systemd, jobs will be created as user services. Otherwise (e.g. running in Docker) jobs are created as Unix daemons and kept alive by the API server.
You may choose the executor with -e (--executor-keys). Supported values are local, systemd, and the keys for any API instance. See the jobs documentation for more information.
- Chain actions with
+. Run multiple commands by joining them with+, similar to&&inbashbut with better performance (one process).
Adding -d (--daemon) will escape these joiners and run all of the chained commands in the job:
- Run chained actions as a pipeline with
:. You can schedule chained actions by adding:to the end of your command:
Other supported flags are --loop, --min-seconds, and the number of times to run the pipeline (e.g. x2 or 2):
-
Add
--restart. Your job will be automatically restarted if you use any of flags--loop,--schedule, or--restart. -
Execute actions remotely. You may execute an action on an API instance by setting the executor to the connector keys. You may run the
executorcommand in the Meercshaum shell (likeinstance) or pass the flag-e(--executor-keys).
The output is streamed directly from the API instance (via a websocket).
- Add
from_plugin_import(). You may now easily access attributes from a plugin's submodule withmeerschaum.plugins.from_plugin_import().
2.2.x Releases¶
Read release notes
The 2.2.x series introduced new features improvements, such as the improved scheduler, the switch to uv, the @dash_plugin and @web_page() decorators, and much more.
v2.2.7¶
-
Fix daemon stability. Broken file handlers are now better handled, and this should keep background jobs from crashing.
-
Improve
show jobsoutput. Theshow jobstable now includes theSuccessTupleof the most recent run (when jobs are stopped). -
Use a plugin's
__doc__string as the default description. When registering a new plugin, the__doc__string will be used as the default value for the description. -
Pipes without connectors are no longer considered errors when syncing. When a pipe has an ordinary string in place of a connector (e.g. externally managed), return early and consider success rather than throwing an error.
import meerschaum as mrsm
pipe = mrsm.Pipe('a', 'b')
pipe.sync()
# (True, "Pipe('a', 'b') does not support fetching; nothing to do.")
-
Add update announcements. When new Meerschaum releases become available, you will now be presented with an update message when starting the shell. Update checks may be disabled by setting
shell:updates:check_remotetofalse. -
Enforce a 10-minute max timeout for
APIConnectors.
v2.2.6¶
-
Fix a critical login issue. The previous release (v2.2.5) broke the login functionality of the Web UI and has been yanked. If you are running v2.2.5, it is urgent that you upgrade immediately.
-
Add environment variable
MRSM_CONFIG_DIR. You may now isolate your configuration directory outside of the root (like withMRSM_PLUGINS_DIR, andMRSM_VENVS_DIR). This will be useful in certain production deployments where secrets need to be segmented and isolated. -
Add
register connector. Likebootstrap connector, you may now programmatically create connectors. -
Allow for job names to contain spaces and parentheses. Jobs may now be created with more dynamic names. This issue in particular affected Meerschaum Compose.
-
Allow for type annotations in
required. Plugins may now annotaterequired:
-
Automatically include
--noaskand--yesin remote actions. For your convenience, the flags--noaskand--yesare included in remote actions sent byAPIConnector.do_action(). -
Fixed an issue with URIs for
apiconnectors. Creating anAPIConnectorvia a URI connection string now properly handles the protocol. -
Fixed a formatting issue with
show logs.
v2.2.5¶
-
Add
bootstrap plugin. Thebootstrap pluginwizard provides a convenient way to create new plugins from templates. -
Add
edit plugin. The actionedit pluginwill open a plugin's source file in your editor ($EDITORorpyvim). -
Allow actions,
fetch(), andsync()to omitpipeand**kwargs. Adding**kwargs(andpipe) is now optional, and you may instead explicitly state only the arguments required.
from meerschaum.actions import make_action
@make_action
def do_the_thing(shell=False):
msg = (
"You're in the mrsm shell."
if shell
else "You called this from bash."
)
return True, msg
# You may now omit the positional argument `pipe`.
def fetch(begin=None, end=None):
return [{'ts': '2024-01-01'}]
- Fixed minor bug with subaction detection. Subaction functions must now explicitly begin with the name of the parent action (underscore prefix allowed).
from meerschaum.actions import make_action
@make_action
def sing():
return True, "~la la la~"
def sing_song():
return True, "~do re mi~"
def _sing_tune():
return True, "~fo so la ti~"
-
Allow for
--begin None. Explicitly setting--begin Nonewill now passNonetofetch(). -
Persist user packages in stack Docker container. The stack Docker Compose file now persists user-level packages (under
~/.local). -
Throw a warning if a
@dash_pluginfunction raises an exception. -
Added connector type to
show connectors. Append a connector type to theshow connectorscommand (e.g.show connectors sql) to see only connectors of a certain type. -
Allow
dprintto be imported frommeerschaum.utils.warnings. For convenience, you may now importdprintalongsideinfo,warn, anderror.
- Add positional arguments filtering (
filter_positional()andfilter_arguments()) In addition to keyword argument filtering, you may now filter positional arguments.
from meerschaum.utils.misc import filter_arguments, filter_positional
def foo(a, b, c=0):
return (a * b) + c
filter_positional(foo, 1, 2, 3)
# (1, 2)
filter_arguments(foo, 1, 2, c=3, d=4)
# ((1, 2), {'c': 3})
- Cleaned up OAuth flow (
/login).
v2.2.2 – v2.2.4¶
-
Speed up package installation in virtual environments. Dynamic dependencies will now be installed via
uv, which dramatically speeds up installation times. -
Add sub-cards for children pipes. Pipes with
childrendefined now include cards for these pipes under the Parameters menu item. This is especially useful when working managing pipeline hierarchies. -
Add "Open in Python" to pipe cards. Clicking "Open in Python" on a pipe's card will now launch
ptpythonwith the pipe object already created.
# Clicking "Open in Python" executes the following:
# $ mrsm python "pipe = mrsm.Pipe('plugin:noaa', 'weather', 'gvl', instance='sql:main')"
>>> import meerschaum as mrsm
>>> pipe = mrsm.Pipe('plugin:noaa', 'weather', 'gvl', instance='sql:main')
- Add the decorators
@web_pageand@dash_plugin. You may now quickly add your own pages to the web console by decorating your layout functions with@web_page:
# example.py
from meerschaum.plugins import dash_plugin, web_page
@dash_plugin
def init_dash(dash_app):
import dash.html as html
import dash_bootstrap_components as dbc
from dash import Input, Output, no_update
@web_page('/my-page', login_required=False)
def my_page():
return dbc.Container([
html.H1("Hello, World!"),
dbc.Button("Click me", id='my-button'),
html.Div(id="my-output-div"),
])
@dash_app.callback(
Output('my-output-div', 'children'),
Input('my-button', 'n_clicks'),
)
def my_button_click(n_clicks):
if not n_clicks:
return no_update
return html.P(f'You clicked {n_clicks} times!')
-
Use
ptpythonfor thepythonaction. Rather than opening a classic REPL, thepythonaction will now open aptpythonshell. -
Allow passing flags to venv
ptpythonbinaries. You may now pass flags directly to theptpythonbinary of a virtual environment (by escaping with[]):
- Allow for custom connectors to implement a
sync()method. Like module-levelsync()functions forpluginconnectors, any custom connector may implementsync()instead offetch().
# example.py
from typing import Any
import meerschaum as mrsm
from meerschaum.connectors import Connector, make_connector
@make_connector
class ExampleConnector(Connector):
def register(self, pipe: mrsm.Pipe) -> dict[str, Any]:
return {
'columns': {
'datetime': 'ts',
'id': 'example_id',
},
}
def sync(self, pipe: mrsm.Pipe, **kwargs) -> mrsm.SuccessTuple:
### Implement a custom sync.
return True, f"Successfully synced {pipe}!"
v2.2.1¶
-
Fix
--schedulein the interactive shell. The--scheduleflag may now be used from both the CLI and the Shell. -
Fix the
SQLConnectorCLI. Thesqlaction now correctly opens an interactive CLI. -
Bumped
duckdbto>=1.0.0. The upstream breaking changes that requiredduckdbto be held back have to do with how indices behave. For now, index creation has been disabled so thatduckdbmay be upgraded to 1.0+.
v2.2.0¶
New Features
- New job scheduler The job scheduler has been rewritten with a simpler syntax.
- Add
show schedule. Validate your schedules' upcoming timestamps withshow schedule.
mrsm show schedule 'daily & mon-fri starting 2024-05-01'
Next 5 timestamps for schedule 'daily & mon-fri starting 2024-05-01':
2024-05-01 00:00:00+00:00
2024-05-02 00:00:00+00:00
2024-05-03 00:00:00+00:00
2024-05-06 00:00:00+00:00
2024-05-07 00:00:00+00:00
- Added timestamps to log file lines.
Log files now prepend the current minute to each line of the file, and the timestamps are also printed when viewing logs with
show logs. To disable this behavio, setMRSM{jobs:logs:timestamps:enabled}tofalse.
You may change the timestamp format under the config keys MRSM{jobs:logs:timestamps:format} (timestamp written to disk) and MRSM{jobs:logs:timestamps:follow_format} (timestamp printed when following via show logs.).
- Add
--skip-deps. When installing plugins, you may skip dependencies with--skip-deps. This should improve the iteration loop during development.
-
Add logs buttons to job cards on the Web UI. For your convenience, "Follow logs" and "Download logs" buttons have been added to jobs' cards.
-
Add a Delete button to job cards on the Web UI. You may now delete a job from its card (once stopped, that is).
-
Add management buttons to pipes' cards. For your convenience, you may now sync, verify, clear, drop, and delete pipes directly from cards.
-
Designate your packages as plugins with the
meerschaum.pluginsentry point. You may now specify your existing packages as Meerschaum plugins by adding themeerschaum.pluginsEntrypoint to your package metadata:
from setuptools import setup
setup(
...,
entry_points = {
'meerschaum.plugins': [
'foo = foo',
],
},
)
or if you are using pyproject.toml:
- Pre- and post-sync hooks are printed separately. The results of sync hooks are now printed right after execution rather than after the sync.
Bugfixes
-
Fixed a filtering bug on the Web UI when changing instances. When changing instances on the Web Console, the connector, metric, and location choices will reset appropriately.
-
Ctrl+C when exiting
show logs. Pressing Ctrl+C will now exit theshow logsimmediately.
Breaking Changes
-
No longer supporting the old scheduler syntax. If you have jobs with the old scheduler syntax (e.g. using the keyword
before), you may need to delete and recreate your jobs with an updated schedule. -
Upgraded to
psycopgfrompsycopg2. The upgrade topsycopg(version 3) should provide better performance for larger transactions. -
Daemon.cleanup()now returns aSuccessTuple.
Other changes
- Bumped
xterm.jsto v5.5.0. - Added tags to the pipes card.
- Replaced
watchgodwithwatchfiles. - Replaced
rocketrywithAPScheduler. - Removed
pydanticfrom dependencies. - Removed
passlibfrom dependencies. - Bumped default TimescaleDB image to
latest-pg16-oss. - Held back
duckdbto<0.10.3.
2.1.x Releases¶
Read release notes
The 2.1.x series added high-performance upserts, improved numerics support and temporary tables performance, and many other bugfixes and improvements.
v2.1.7¶
- Add
query_df()tomeerschaum.utils.dataframe. The functionquery_df()allows you to filter dataframes byparams,begin, andend.
import pandas as pd
df = pd.DataFrame([
{'a': 1, 'color': 'red'},
{'a': 2, 'color': 'blue'},
{'a': 3, 'color': 'green'},
{'a': 4, 'color': 'yellow'},
])
from meerschaum.utils.dataframe import query_df
query_df(df, {'color': ['red', 'yellow']})
# a color
# 0 1 red
# 3 4 yellow
query_df(df, {'color': '_blue'}, reset_index=True)
# a color
# 0 1 red
# 1 3 green
# 2 4 yellow
query_df(df, {'a': 2}, select_columns=['color'])
# color
# 1 blue
get_in_ex_params() to meerschaum.utils.misc.
This function parses a standard params dictionary into tuples of include and exclude parameters.
from meerschaum.utils.misc import get_in_ex_params
params = {'color': ['red', '_blue', 'green']}
in_ex_params = get_in_ex_params(params)
in_ex_params
# {'color': (['red', 'green'], ['blue'])}
in_vals, ex_vals = in_ex_params['color']
-
Add
coerce_numerictopipe.enforce_dtypes(). Setting this toFalsewill not cast floats toDecimalif the corresponding dtype isint. -
Improve JSON serialization when filtering for updates.
-
Add
date_bound_onlytopipe.filter_existing(). The argumentdate_bound_onlymeans that samples retrieved bypipe.get_data()will only usebeginandendfor bounding. This may improve performance for custom instance connectors which have limited searchability. -
Add
safe_copytopipe.enforce_types(),pipe.filter_existing(),filter_unseen_df(). By default, these functions will create copies of dataframes to avoid mutating the input dataframes. Settingsafe_copytoFalsemay be more memory efficient. -
Add multiline support to
extract_stats_from_message. Multiple messages separated by newlines may be parsed at once.
from meerschaum.utils.formatting import extract_stats_from_message
extract_stats_from_message("Inserted 10, upserted 3\ninserted 11, upserted 4")
# {'inserted': 21, 'updated': 0, 'upserted': 7}
-
Remove
order bycheck in SQL queries. -
Improve shell startup performance by removing support for
cmd2. The packagecmd2never behaved properly, so support has been removed and only the built-incmdpowers the shell. As such, the configuration keyshell:cmdhas been removed.
v2.1.6¶
- Move
success_tuplefrom arg to kwarg for@post_sync_hookfunctions. To match the signature of@pre_sync_hookfunctions,@post_sync_hookfunctions now only acceptpipeas the positional argument. The return value of the sync will now be passed as the kwargsuccess_tuple. This allows you to use the same callback function as both the pre- and post-sync hooks.
import meerschaum as mrsm
from meerschaum.plugins import pre_sync_hook, post_sync_hook
@pre_sync_hook
@post_sync_hook
def log_sync(
pipe: mrsm.Pipe,
success_tuple: mrsm.SuccessTuple | None = None,
):
if success_tuple is None:
print(f"About to sync {pipe}!")
else:
success, msg = success_tuple
print(f"Finished syncing {pipe} with message:\n{msg}")
- Add
sync_timestampandsync_complete_timestampto sync hooks. The UTC datetime right before the sync is added to the sync hook kwargs, allowing for linking the two callbacks to the same datetime. For convenience, the UTC datetime is also captured at the end of the sync and is passed assync_complete_timestamp.
from datetime import datetime
import meerschaum as mrsm
from meerschaum.plugins import pre_sync_hook, post_sync_hook
@pre_sync_hook
@post_sync_hook
def log_sync(
pipe: mrsm.Pipe,
sync_timestamp: datetime | None = None,
sync_complete_timestamp: datetime | None = None,
sync_duration: float | None = None,
) -> mrsm.SuccessTuple:
if sync_complete_timestamp is None:
print(f"About to sync {pipe} at {sync_timestamp}.")
return True, "Success"
msg = (
f"It took {sync_duration} seconds to sync {pipe}:\n"
+ "({sync_timestamp} - {sync_complete_timestamp})"
)
return True, msg
-
Improved performance of sync hooks. Sync hooks are now called asynchronously in their own threads to avoid slowing down or crashing the main thread.
-
Rename
durationtosync_durationfor sync hooks. To avoid potential conflicts, the kwargdurationis prefixed withsync_to denote that it was specifically added to provide context on the sync. -
Allow for sync hooks to return
SuccessTuple. If a sync hook returns aSuccessTuple(Tuple[bool, str]), the result will be printed.
import meerschaum as mrsm
from meerschaum.plugins import post_sync_hook
@post_sync_hook
def log_sync(pipe) -> mrsm.SuccessTuple:
return True, f"Logged sync for {pipe}."
- Add
is_success_tuple()tomeerschaum.utils.typing. You can now quickly check whether an object is aSuccessTuple:
from meerschaum.utils.typing import is_success_tuple
assert is_success_tuple((True, "Success"))
assert not is_success_tuple(("foo", "bar"))
- Allow for index-only pipes when
upsert=True. If all columns are indices andupsertisTrue, then the upsert will insert net-new rows (ignore duplicates).
import meerschaum as mrsm
pipe = mrsm.Pipe(
'a', 'b',
columns = ['a', 'b'],
parameters = {'upsert': True},
instance = 'sql:local',
)
pipe.sync([{'a': 1, 'b': 2}])
-
Allow for prelimary null index support for
upsert=True(inserts only, PostgreSQL only). Like with regular syncs, upsert syncs now coalesce indices to allow for syncing null values. NOTE: the transaction will fail if a null index is synced again, so this is only for the initial insert. -
Remove automatic instance table renaming. This patch removes automatic detection and renaming of old instance tables to the new names (e.g.
users->mrsm_users). Users migrating from an old installation will need to rename the tables manually themselves.
v2.1.5¶
- Add the action
tag pipes. Tags may be added or removed with thetag pipesaction. Note that the flag--tagsapplies to existing tags for filtering; flags to be added or removed are positional arguments.
### Tag all `sql:main` pipes with `production` and `sync-daily`.
mrsm tag pipes production sync-daily -c sql:main
### Remove the `production`` tag from all pipes tagged as `production`.
mrsm tag pipes _production --tags production
-
Add
--tagssupport toregister pipes. The actionregister pipewith the--tagsflag will auto-tag the new pipes. -
Clean up warnings on Python 3.12. All instances of
datetime.utcnow()have been replaced bydatetime.now(timezone.utc).replace(tzinfo=None)(to preserve behavior). A full migration to timezone-aware datetimes would have to happen in a minor release. -
Improve timezone-aware datetime support for MSSQL. Passing a timezone-aware datetime as a date bound for MSSQL should now be fixed.
-
Add an explicit
VOLUMEto theDockerfile. The path/meerschaumis now explicitly set as aVOLUMEin the Docker image. -
Add
--tagsfiltering to theshow tagsaction. -
Improve global
ThreadPoolhandling. Global pools are now created on a per-worker (and per-class) basis, allowing for switching between workers within the same process. Note that global pools are maintained to allow for nested chunking and the limit the number of connections (e.g. avoid threads creating their own pools). -
Fix a bug when selecting only by negating tags. Pipes may now be selected by only specifying negated tags.
-
Rename
meerschaum.utils.get_pipestomeerschaum.utils._get_pipesto avoid namespace collissions. The functionsget_pipes()andfetch_pipes_keys()are available at themeerschaum.utilsmodule namespace.
v2.1.3 – v2.1.4¶
- Add the decorators
@pre_sync_hookand@post_sync_hook. The new decorators@pre_sync_hookand@post_sync_hooklet you intercept a Pipe immediately before and after a sync, capturing its return tuple and the duration in seconds.
import meerschaum as mrsm
from meerschaum.plugins import pre_sync_hook, post_sync_hook
@pre_sync_hook
def prepare_for_sync(pipe: mrsm.Pipe, **kwargs):
print(f"About to sync {pipe} with kwargs:\n{kwargs}")
@post_sync_hook
def log_sync(
pipe: mrsm.Pipe,
return_tuple: mrsm.SuccessTuple,
duration: float = 0.0
):
print(f"It took {round(duration, 2)} seconds to sync {pipe} with result:")
mrsm.pprint(return_tuple)
-
Add the action
show tags. The actionshow tagswill now display panels of pipes grouped together by common tags. This is useful for large deployments which share common tags. -
Add dropdowns and inputs for flags with arguments to the Web Console. Leverage the full power of the Meerschaum CLI in the Web Console with the new dynamic flags dropdowns.
-
Fix shell crashes in Docker containers. Reloading the running Meerschaum session from an interactive shell via a Docker container will no longer cause crashes on custom commands.
-
Improve reloading times. Reloading the running Meerschaum session has been sped up by several seconds (due to skipping the internal shell modules).
-
Improve virtual environments in the Docker image. Initial startup of Docker containers on a fresh persistent volume has been sped up due to preloading the default virtual environment creation. Additionally, the environment variable
$MRSM_VENVS_DIRhas been unset, reverting the virtual environments to be stored under/meerschaum/venvs.
v2.1.1 – v2.1.2¶
- Add
upsertfor high-performance pipes. Settingupsertunderpipe.parameterswill create a unique index and combine the insert and update stages into a single upsert. This is particularly useful for pipes with very large tables.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'upsert',
instance = 'sql:local',
columns = ['datetime', 'id'],
parameters = {'upsert': True},
)
mrsm.pprint(
pipe.sync(
[
{'datetime': '2023-01-01', 'id': 1, 'val': 1.1},
{'datetime': '2023-01-02', 'id': 2, 'val': 2.2},
]
)
)
# 🎉 Upserted 2 rows.
-
Add internal schema
_mrsm_internalfor temporary tables. To avoid polluting your database schema, temporary tables will now be created within_mrsm_internal(for databases which support PostgreSQL-style schemas). -
Added
mrsm_temporary_tables. Temporary tables are logged tomrsm_temporary_tablesand deleted when dropped. -
Drop stale temporary tables. Temporary tables which are more than 24 hours will be dropped automatically (configurable under
system:sql:instance:stale_temporary_tables_minutes). -
Prefix instance tables with
mrsm_. Internal Meerschaum tables still reside on the default schema but will be denoted with amrsm_prefix. The existing instance tables will be automatically renamed (e.g.pipeswill becomemrsm_pipes) to add the prefix (this renaming detection will be removed in later releases). -
Fix an issue with
bootstrap. Refactoring work for 2.1.0 had broken thebootstrapaction. -
Fix an issue with
pause jobs. -
Fix an issue when selecting inverse pipes. Null location keys are now coalesced when selecting pipes to produce expected behavior.
-
Avoid a system exit when exiting the SQL CLI.
v2.1.0¶
-
Replace
term.jswithxterm.js. This has been a long time coming. The webterm has been migrated toxterm.jswhich has continuous support fromterm.jswhich was last updated almost 10 years ago. -
Deprecate the legacy web pseudo-terminal. Clicking the "Execute" button on the web console will now execute the command directly in the webterm. Additionally, changing the instance select will now automatically switch the webterm's context to the desired instance.
-
Fix an issue when starting existing jobs. A bug has been fixed which prevented jobs from restarting specifically by name.
-
Add
MRSM_VENVS_DIR. LikeMRSM_PLUGINS_DIR, you can now designate a virtual environments directory separate from the root directory. This is particularly useful for production deployments, andMRSM_VENVS_DIRhas been set to/home/meerschaum/venvsin the official Docker images to allow for mounting/meerschaumto persistent volumes. -
Allow syncing
NULLvalues into indices. SyncingNonewithin an index will now be coalesced into a magic value when applying updates.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'allow', 'null', 'indices',
instance = 'sql:local',
columns = ['a', 'b'],
)
pipe.sync([{'a': 1, 'b': 1}])
pipe.sync([{'b': 1}])
pipe.sync([{'a': 1}])
pipe.sync([{'c': 1}])
print(pipe.get_data())
# a b c
# 0 <NA> <NA> 1
# 1 <NA> 1 <NA>
# 2 1 <NA> <NA>
# 3 1 1 <NA>
- Syncing
Decimalobjects will now enforcenumericdtypes. For example, syncing aDecimalonto a integer column will update the dtype tonumeric, like when syncing a float after an integer.
import meerschaum as mrsm
from decimal import Decimal
pipe = mrsm.Pipe(
'demo', 'decimal', 'coersion',
instance = 'sql:local',
columns = ['id'],
)
pipe.sync([{'id': 1, 'foo': 10}])
pipe.sync([{'id': 1, 'foo': Decimal('20')}])
print(pipe.dtypes)
# {'id': 'int64[pyarrow]', 'foo': 'numeric'}
df = pipe.get_data()
print(f"{df['foo'][0]=}")
# df['foo'][0]=Decimal('20')
- Improve
IS NULLandIS NOT NULLchecks forparams. Mixing null-like values (e.g.NaN,<NA>,None) inparamswill now separate out nulls.
from meerschaum.utils.sql import build_where
print(build_where({'a': ['_<NA>', '_1', '_2']}))
# WHERE
# ("a" NOT IN ('1', '2')
# AND "a" IS NOT NULL)
print(build_where({'a': ['NaN', '1', '2']}))
# WHERE
# ("a" IN ('1', '2')
# OR "a" IS NULL)
-
Add colors to
mrsm show columns. -
Fix a unicode decoding error when showing logs.
-
Remove
xstaticdependencies. Thexterm.jsfiles are now bundled as static assets, so theterm.jsfiles are no longer needed. Hurray for removing dependencies! -
Other bugfixes. A handful of minor bugfixes have been included in this release:
- Removed non-connector environment variables like
MRSM_WORK_DIRfrom themrsm show connectorsoutput. - Improving symlinks handling for multi-processed situations (
mrsm start api).
- Removed non-connector environment variables like
2.0.x Releases¶
Read release notes
At long last, 2.0 has arrived! The 2.0 releases brought incredible change, from standardizing chunking to adding Pipe.verify() and Pipe.deduplicate() to introducing first-class numeric support. See the full release notes below for the complete picture.
v2.0.8 – v2.0.9¶
-
Cast
NonetoDecimal('NaN')fornumericcolumns. To allow for all-null numeric columns,None(and other null-like types) are coerced toDecimal('NaN'). -
Schema bugfixes. A few minor edge cases have been addressed when working with custom schemas for pipes.
-
Remove
APIConnector.get_backtrack_data(). Since 1.7 released, theget_backtrack_data()method for instance connectors has been optional.NOTE: the
backtrack_dataAPI endpoint has also been removed. -
Other bugfixes. Issues with changes made to session authentication have been addressed.
v2.0.5 – v2.0.7¶
- Add the
numericdtype (i.e. support forNUMERICcolumns). Specifying a column asnumericwill coerce it intodecimal.Decimalobjects. ForSQLConnectors, this will be stored as aNUMERICcolumn. This is useful for syncing a mix of integer and float values.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'numeric',
instance = 'sql:main',
columns = ['foo'],
dtypes = {'foo': 'numeric'},
)
pipe.sync([{'foo': '1'}, {'foo': '2.01234567890123456789'}])
df = pipe.get_data()
print(df.to_dict(orient='records'))
# [{'foo': Decimal('1')}, {'foo': Decimal('2.01234567890123456789')}]
NOTE: Due to implementation limits,
numerichas strict precision issues in embedded databases (SQLite and DuckDB:NUMERIC(15, 4)). PostgreSQL-like database flavors have the best support forNUMERIC; MySQL and MariaDB use a scale and precision ofNUMERIC(38, 20), MSSQL usesNUMERIC(28, 10), and Oracle and PostgreSQL are not capped.
- Mixing
intandfloatwill cast tonumeric. Rather than always casting toTEXT, a column containing a mix ofintandfloatwill be coerced intonumeric.
import meerschaum as mrsm
pipe = mrsm.Pipe('mix', 'int', 'float', instance='sql:local')
pipe.sync([{'a': 1}])
pipe.sync([{'a': 1.1}])
print(pipe.dtypes)
# {'a': 'numeric'}
- Add
schematoSQLConnectors. Including the keyschemaor as an argument in the URI will use this schema for created tables. The argumentsearch_pathwill also setschema(i.e. for PostgreSQL).
export MRSM_SQL_FOO='{
"username": "foo",
"password": "bar",
"port": 5432,
"flavor": "timescaledb",
"host": "localhost",
"database": "db",
"schema": "myschema"
}'
export MRSM_SQL_FOO='timescaledb://foo:bar@localhost:5432/db?options=--search_path%3Dmyschema'
- Add
schematopipe.parameters. In addition to the default schema at the connector level, you may override this by settingschemaunderpipe.parameters.
- Add
schematomeerschaum.utils.sql.sql_item_name(). You may now pass an optionalschemawhen quoting:
from meerschaum.utils.sql import sql_item_name
print(sql_item_name('foo', 'mssql', schema='dbo'))
# '[dbo].[foo]'
-
Add
optionstoSQLConnector. The keyoptionswill now contain a sub-dictionary of connection options, such asdriver,search_path, or any other query parameters. -
Disable the "Sync Documents" accordion item when the session is not authenticated. When running the API with
--secure, only admin users will be able to access the "Sync Documents" accordion items on the pipes' cards. -
Remove
dtype_backendfromSQLConnector.read(). This argument previously had no effect. When applied, it was coercing JSON columns into strings, so it was removed. -
Remove
meerschaum.utils.daemon.Log. This had been replaced bymeerschaum.utils.daemon.RotatingLogand had been broken since the 2.0 release. -
Remove
paramsfromPipe.filter_existing(). To avoid confusion, filter parameters are instead derived from the incoming DataFrame. This will improve performance when repeatedly syncing chunks which span the same interval. The default limit of 250 unique values may be configured underpipes:sync:filter_params_index_limit. -
Add
forwarded_allow_ipsandproxy_headersto the web API. The default valuesforwarded_allow_ips='*'andproxy_headers=Trueare set when running Uvicorn or Gunicorn and will help when running Meerschaum behind a proxy. -
Bump
dash-extensionsto>=1.0.4. The bug that was holding back the version was due to includingenrich.ServersideTransformin the dash proxy without actually utilizing it.
v2.0.3 – v2.0.4¶
-
Fix an issue with
--timeout-seconds. Previous refactoring efforts had broken the--timeout-secondspolling behavior. -
Fix a formatting issue when pretty-printing pipes. Pipes may now be correctly printed if both single and double quotes appear in a message.
-
Allow omitting
portforAPIConnectors. You may now omit theportattribute forAPIConnectorsto use the protocol-default port (e.g. 443 for HTTPS). Note you will need to delete the keyapi:default:portviamrsm edit configif it's present. -
Add optional
verifykey to API connectors. Client API connectors may now be used with self-signed HTTPS instances. -
Bump
duckdbto version 0.9.0. This adds complete support for PyArrow data types to DuckDB.
v2.0.2¶
-
Syncing with
--skip-check-existingwill not apply the backtrack interval. Because--skip-check-existing(orcheck_existing=False) is guaranteed to produce duplicates, the backtrack interval will be set to 0 when running in insert-only mode. -
Allow for
columnsto be a list. Note that building a pipe withcolumnsas a list must have the datetime column nameddatetime.
import meerschaum as mrsm
pipe = mrsm.Pipe('a', 'b', columns=['a'])
print(pipe.columns)
# {'a': 'a'}
-
Bump default SQLAlchemy pool size to 8 connections.
-
Consider the number of checked out connections when choosing workers. For pipes on
sqlinstances,pipe.get_num_workers()will now consider the number of checked out connections rather than only the number of active threads. -
Fix
pipe.get_data(as_dask=True)for JSON columns.
v2.0.1¶
-
Fix syncing bools within in-place SQL pipes. SQL pipes may now sync bools in-place. For database flavors which lack native
BOOLEANsupport (e.g.sqlite,oracle,mysql), then the boolean columns must be stated inpipe.dtypes. -
Fix an issue with multiple users managing jobs. Extra validation was added to the web UI to allow for multiple users to interact with jobs.
-
Fix a minor formatting bug with
highlight_pipes(). Improved validation logic was added to prevent incorrectly prepending thePipe(prefix. -
Hold back
pydanticto<2.0.0Pydantic 2 is supported in all features except--schedule. Untilrocketrysupports Pydantic 2, it will be held back.
v2.0.0¶
Breaking Changes
-
Removed redundant
Pipe.sync_timeproperty. Usepipe.get_sync_time()instead. -
Removed
SQLConnector.get_pipe_backtrack_minutes(). Usepipe.get_backtrack_interval()instead. -
Replaced
pipe.parameters['chunk_time_interval']withpipe.parameters['verify']['chunk_minutes']For better security and cohesiveness, the TimescaleDBchunk_time_intervalvalue is now derived from the standardchunk_minutesvalue. This also means pipes with integer date axes will be created with a new default chunk interval of 1440 (was previously 100,000). -
Moved
choose_subaction()intomeerschaum.actions. This function is for internal use and as such should not affect any users.
Features
- Added
verify pipesand--verify. The commandmrsm verify pipesormrsm sync pipes --verifywill resync pipes' chunks with different rowcounts to catch any backfilled data.
import meerschaum as mrsm
foo = mrsm.Pipe(
'foo', 'src',
target = 'foo',
columns = {'datetime': 'dt'},
instance = 'sql:local'
)
docs = [
{'dt': '2023-01-01'},
{'dt': '2023-01-02'},
]
foo.sync(docs)
pipe = mrsm.Pipe(
'sql:local', 'verify',
columns = {'datetime': 'dt'},
parameters = {
'query': f'SELECT * FROM "{foo.target}"'
},
instance = 'sql:local',
)
pipe.sync(docs)
backfilled_docs = [
{'dt': '2022-12-30'},
{'dt': '2022-12-31'},
]
foo.sync(backfilled_docs)
mrsm.pprint(pipe.verify())
assert foo.get_rowcount() == pipe.get_rowcount()
- Added
deduplicate pipesand--deduplicate. Runningmrsm deduplicates pipesormrsm sync pipes --deduplicatewill iterate over pipes' entire intervals, chunking at the configured chunk interval (seepipe.get_chunk_interval()below) and clearing + resyncing chunks with duplicate rows.
If your instance connector implements deduplicate_pipe() (e.g. SQLConnector), then this method will override the default pipe.deduplicate().
pipe = mrsm.Pipe(
'demo', 'deduplicate',
columns = {'datetime': 'dt'},
instance = 'sql:local',
)
docs = [
{'dt': '2023-01-01'},
{'dt': '2023-01-01'},
]
pipe.sync(docs)
print(pipe.get_rowcount())
# 2
pipe.deduplicate()
print(pipe.get_rowcount())
# 1
- Added
pyarrowsupport. The dtypes enforcement system was overhauled to add support forpyarrowdata types.
import meerschaum as mrsm
import pandas as pd
df = pd.DataFrame(
[{'a': 1, 'b': 2.3}]
).convert_dtypes(dtype_backend='pyarrow')
pipe = mrsm.Pipe(
'demo', 'pyarrow',
instance = 'sql:local',
columns = {'a': 'a'},
)
pipe.sync(df)
- Added
boolsupport. Pipes may now sync DataFrames with booleans (even on Oracle and MySQL):
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'bools',
instance = 'sql:local',
columns = {'id': 'id'},
)
pipe.sync([{'id': 1, 'is_blue': True}])
assert 'bool' in pipe.dtypes['is_blue']
pipe.sync([{'id': 1, 'is_blue': False}])
assert pipe.get_data()['is_blue'][0] == False
- Added preliminary
dasksupport. For example, you may now return Dask DataFrames in your plugins, pass intopipe.sync(), andpipe.get_data()now has the flagas_dask.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'dask', 'demo',
columns = {'datetime': 'dt'},
instance = 'sql:local',
)
pipe.sync([
{'dt': '2023-01-01', 'val': 1},
{'dt': '2023-01-02', 'val': 2},
{'dt': '2023-01-03', 'val': 3},
])
ddf = pipe.get_data(as_dask=True)
print(ddf)
# Dask DataFrame Structure:
# dt val
# npartitions=4
# datetime64[ns] int64[pyarrow]
# ... ...
# ... ...
# ... ...
# ... ...
# Dask Name: from-delayed, 5 graph layers
print(ddf.compute())
# dt val
# 0 2023-01-01 1
# 0 2023-01-02 2
# 0 2023-01-03 3
pipe2 = mrsm.Pipe(
'dask', 'insert',
columns = pipe.columns,
instance = 'sql:local',
)
pipe2.sync(ddf)
assert pipe.get_data().to_dict() == pipe2.get_data().to_dict()
pipe.sync([{'dt': '2023-01-01', 'val': 10}])
pipe2.sync(pipe.get_data(as_dask=True))
assert pipe.get_data().to_dict() == pipe2.get_data().to_dict()
- Added
chunk_minutestopipe.parameters['verify']. Likepipe.parameters['fetch']['backtrack_minutes'], you may now specify the default chunk interval to use for verification syncs and iterating over the datetime axis.
import meerschaum as mrsm
pipe = mrsm.Pipe(
'a', 'b',
instance = 'sql:local',
columns = {'datetime': 'dt'},
parameters = {
'verify': {
'chunk_minutes': 2880,
}
},
)
pipe.sync([
{'dt': '2023-01-01'},
{'dt': '2023-01-02'},
{'dt': '2023-01-03'},
{'dt': '2023-01-04'},
{'dt': '2023-01-05'},
])
chunk_bounds = pipe.get_chunk_bounds(bounded=True)
for chunk_begin, chunk_end in chunk_bounds:
print(chunk_begin, '-', chunk_end)
# 2023-01-01 00:00:00 - 2023-01-03 00:00:00
# 2023-01-03 00:00:00 - 2023-01-05 00:00:00
- Added
--chunk-minutes,--chunk-hours, and--chunk-days. You may override a pipe's chunk interval during a verification sync with--chunk-minutes(or--chunk-hoursor--chunk-days).
- Added
pipe.get_chunk_interval()andpipe.get_backtrack_interval(). Return thetimedelta(orintfor integer datetimes) fromverify:chunk_minutesandfetch:backtrack_minutes, respectively.
import meerschaum as mrsm
dt_pipe = mrsm.Pipe(
'demo', 'intervals', 'datetime',
instance = 'sql:local',
columns = {'datetime': 'dt'},
)
print(dt_pipe.get_chunk_interval())
# 1 day, 0:00:00
int_pipe = mrsm.Pipe(
'demo', 'intervals', 'int',
instance = 'sql:local',
columns = {'datetime': 'dt'},
dtypes = {'dt': 'int'},
)
print(int_pipe.get_chunk_interval())
# 1440
- Added
pipe.get_chunk_bounds(). Return a list ofbeginandendvalues to use when iterating over a pipe's datetime axis.
from datetime import datetime
import meerschaum as mrsm
pipe = mrsm.Pipe(
'demo', 'chunk_bounds',
instance = 'sql:local',
columns = {'datetime': 'dt'},
parameters = {
'verify': {
'chunk_minutes': 1440,
}
},
)
pipe.sync([
{'dt': '2023-01-01'},
{'dt': '2023-01-02'},
{'dt': '2023-01-03'},
{'dt': '2023-01-04'},
])
open_bounds = pipe.get_chunk_bounds()
for i, (begin, end) in enumerate(open_bounds):
print(f"Chunk {i}: ({begin}, {end})")
# Chunk 0: (None, 2023-01-01 00:00:00)
# Chunk 1: (2023-01-01 00:00:00, 2023-01-02 00:00:00)
# Chunk 2: (2023-01-02 00:00:00, 2023-01-03 00:00:00)
# Chunk 3: (2023-01-03 00:00:00, 2023-01-04 00:00:00)
# Chunk 4: (2023-01-04 00:00:00, None)
closed_bounds = pipe.get_chunk_bounds(bounded=True)
for i, (begin, end) in enumerate(closed_bounds):
print(f"Chunk {i}: ({begin}, {end})")
# Chunk 0: (2023-01-01 00:00:00, 2023-01-02 00:00:00)
# Chunk 1: (2023-01-02 00:00:00, 2023-01-03 00:00:00)
# Chunk 2: (2023-01-03 00:00:00, 2023-01-04 00:00:00)
sub_bounds = pipe.get_chunk_bounds(
begin = datetime(2023, 1, 1),
end = datetime(2023, 1, 3),
)
for i, (begin, end) in enumerate(sub_bounds):
print(f"Chunk {i}: ({begin}, {end})")
# Chunk 0: (2023-01-01 00:00:00, 2023-01-02 00:00:00)
# Chunk 1: (2023-01-02 00:00:00, 2023-01-03 00:00:00)
- Added
--boundedto verification syncs. By default,verify pipesis unbounded, meaning it will sync values beyond the existing minimum and maximum datetime values. Running a verification sync with--boundedwill bound the search to the existing datetime axis.
-
Added
pipe.get_num_workers(). Return the number of concurrent threads to be used with this pipe (with respect to its instance connector's thread safety). -
Added
select_columnsandomit_columnstopipe.get_data(). In situations where not all columns are required, you can now either specify which columns you want to include (select_columns) and which columns to filter out (omit_columns). You may pass a list of columns or a single column, and the value'*'forselect_columnswill be treated asNone(i.e.SELECT *).
pipe = mrsm.Pipe('a', 'b', 'c', instance='sql:local')
pipe.sync([{'a': 1, 'b': 2, 'c': 3}])
pipe.get_data(['a', 'b'])
# a b
# 0 1 2
pipe.get_data('*', 'b')
# a c
# 0 1 3
pipe.get_data(None, ['a', 'c'])
# b
# 0 2
pipe.get_data(omit_columns=['b', 'c'])
# a
# 0 1
pipe.get_data(select_columns=['c', 'a'])
# c a
# 0 3 1
-
Replace
daemonikerwithpython-daemon.python-daemonis a well-maintained and well-behaved daemon process library. However, this migration removes Windows support for background jobs (which was never really fully supported already, so no harm there). -
Added
pause jobs. In addition tostart jobsandstop jobs, the commandpause jobswill suspend a job's daemon. Jobs may be resumed withstart jobs(i.e.Daemon.resume()). -
Added job management to the UI. Now that jobs and logs are much more robust, more job management features have been added to the web UI. Jobs may be started, stopped, paused, and resumed from the web console, and their logs are now available for download.
-
Logs now roll over and are preserved on job restarts. Spin up long-running job with peace of mind now that logs are automatically rolled over, keeping five 500 KB files on disk at any moment (you can tweak these values with
mrsm edit config jobs). To facilitate this,meershaum.utils.daemon.RotatingFilewas added to provide a generic file-like object, complete with its own file descriptor. -
Starting existing jobs with
-dwill not throw an exception if the arguments match. Similarly, running without any arguments other than--namewill run the existing job. This matches the behavior ofstart jobs. -
Allow for colon-separated paths in
MRSM_PLUGINS_DIR. Just likePATHinbash, you may now specify your plugins' paths in a single variable, separated by colons. Unlikebash, however, a blank path will not interpreted as the current directory.
- Add
pipe.keys()pipe.keys()returns the connector, metric, and location keys (i.e.pipe.metawithout theinstance).
pipe = mrsm.Pipe('foo', 'bar')
print(pipe.keys())
# {'connector': 'foo', 'metric': 'bar', 'location': None}
- Pipes are now indexable.
Indexing a pipe directly is the same as accessing
pipe.attributes:
pipe = mrsm.Pipe('a', 'b', columns={'foo': 'bar'})
print(pipe['connector'])
# 'a'
print(pipe['connector_keys'])
# 'a'
print(pipe['columns'])
# {'foo': 'bar'}
print(pipe['does_not_exist'])
# None
Other changes
-
Fixed backtracking being incorrectly applied to
--begin. Application of the backtracking interval has been consolidated intopipe.fetch(). -
Improved data type enforcement for SQL pipes. A pipe's data types are now passed to
SQLConnector.read()when fetching its data. -
Added
meerschaum.utils.sql.get_db_version()andSQLConnector.db_version. -
Moved
print_options()frommeerschaum.utils.miscintomeerschaum.utils.formatting. This placesprint_options()next toprint_tupleandpprint. A placeholder function is still present inmeerschaum.utils.miscto preserve existing behavior. -
mrsm.pprint()will now pretty-printSuccessTuples. -
Added
calmtoprint_tuple(). Printing aSuccessTuplewithcalm=Truewill use a more muted color scheme and emoji. -
Removed
round_downfromget_sync_time()for instance connectors. To avoid confusion, sync times are no longer truncated by default.round_downis still an optional keyword argument onpipe.get_sync_time(). -
Created
meerschaum.utils.dtypes. - Added
are_dtypes_equal()tomeerschaum.utils.dtypes. - Added
get_db_type_from_pd_type()tomeerschaum.utils.dtypes.sql. - Added
get_pb_type_from_db_type()tomeerschaum.utils.dtypes.sql. -
Moved
to_pandas_dtype()frommeerschaum.utils.miscintomeerschaum.utils.dtypes. -
Created
meerschaum.utils.dataframe. - Added
chunksize_to_npartitions()tomeerschaum.utils.dataframe. - Added
get_first_valid_dask_partition()tomeerschaum.utils.dataframe. - Moved
filter_unseen_df()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. - Moved
add_missing_cols_to_df()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. - Moved
parse_df_datetimes()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. - Moved
df_from_literal()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. - Moved
get_json_cols()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. - Moved
get_unhashable_cols()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. - Moved
enforce_dtypes()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. - Moved
get_datetime_bound_from_df()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. -
Moved
df_is_chunk_generator()frommeerschaum.utils.miscintomeerschaum.utils.dataframe. -
Refactored SQL utilities.
- Added
format_cte_subquery()tomeerschaum.utils.sql. - Added
get_create_table_query()tomeerschaum.utils.sql. - Added
get_db_version()tomeerschaum.utils.sql. -
Added
get_rename_table_queries()tomeerschaum.utils.sql. -
Moved
choices_docstring()frommeerschaum.utils.miscintomeerschaum.actions. - Fixed handling backslashes for
stackon Windows.