exonware-xwsystem 0.0.1.411__py3-none-any.whl → 0.1.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exonware/__init__.py +2 -1
- exonware/conf.py +2 -2
- exonware/xwsystem/__init__.py +115 -43
- exonware/xwsystem/base.py +30 -0
- exonware/xwsystem/caching/__init__.py +39 -13
- exonware/xwsystem/caching/base.py +24 -6
- exonware/xwsystem/caching/bloom_cache.py +2 -2
- exonware/xwsystem/caching/cache_manager.py +2 -1
- exonware/xwsystem/caching/conditional.py +2 -2
- exonware/xwsystem/caching/contracts.py +85 -139
- exonware/xwsystem/caching/decorators.py +6 -19
- exonware/xwsystem/caching/defs.py +2 -1
- exonware/xwsystem/caching/disk_cache.py +2 -1
- exonware/xwsystem/caching/distributed.py +2 -1
- exonware/xwsystem/caching/errors.py +2 -1
- exonware/xwsystem/caching/events.py +110 -27
- exonware/xwsystem/caching/eviction_strategies.py +2 -2
- exonware/xwsystem/caching/external_caching_python.py +701 -0
- exonware/xwsystem/caching/facade.py +253 -0
- exonware/xwsystem/caching/factory.py +300 -0
- exonware/xwsystem/caching/fluent.py +14 -12
- exonware/xwsystem/caching/integrity.py +21 -6
- exonware/xwsystem/caching/lfu_cache.py +2 -1
- exonware/xwsystem/caching/lfu_optimized.py +18 -6
- exonware/xwsystem/caching/lru_cache.py +7 -4
- exonware/xwsystem/caching/memory_bounded.py +2 -2
- exonware/xwsystem/caching/metrics_exporter.py +2 -2
- exonware/xwsystem/caching/observable_cache.py +2 -2
- exonware/xwsystem/caching/pluggable_cache.py +2 -2
- exonware/xwsystem/caching/rate_limiter.py +2 -2
- exonware/xwsystem/caching/read_through.py +2 -2
- exonware/xwsystem/caching/secure_cache.py +81 -28
- exonware/xwsystem/caching/serializable.py +9 -7
- exonware/xwsystem/caching/stats.py +2 -2
- exonware/xwsystem/caching/tagging.py +2 -2
- exonware/xwsystem/caching/ttl_cache.py +4 -3
- exonware/xwsystem/caching/two_tier_cache.py +6 -3
- exonware/xwsystem/caching/utils.py +30 -12
- exonware/xwsystem/caching/validation.py +2 -2
- exonware/xwsystem/caching/warming.py +6 -3
- exonware/xwsystem/caching/write_behind.py +15 -6
- exonware/xwsystem/config/__init__.py +11 -17
- exonware/xwsystem/config/base.py +5 -5
- exonware/xwsystem/config/contracts.py +93 -153
- exonware/xwsystem/config/defaults.py +3 -2
- exonware/xwsystem/config/defs.py +3 -2
- exonware/xwsystem/config/errors.py +2 -5
- exonware/xwsystem/config/logging.py +12 -8
- exonware/xwsystem/config/logging_setup.py +3 -2
- exonware/xwsystem/config/performance.py +73 -391
- exonware/xwsystem/config/performance_modes.py +9 -8
- exonware/xwsystem/config/version_manager.py +1 -0
- exonware/xwsystem/config.py +27 -0
- exonware/xwsystem/console/__init__.py +53 -0
- exonware/xwsystem/console/base.py +133 -0
- exonware/xwsystem/console/cli/__init__.py +61 -0
- exonware/xwsystem/{cli → console/cli}/args.py +27 -24
- exonware/xwsystem/{cli → console/cli}/base.py +18 -87
- exonware/xwsystem/{cli → console/cli}/colors.py +15 -13
- exonware/xwsystem/console/cli/console.py +98 -0
- exonware/xwsystem/{cli → console/cli}/contracts.py +51 -69
- exonware/xwsystem/console/cli/defs.py +87 -0
- exonware/xwsystem/console/cli/encoding.py +69 -0
- exonware/xwsystem/{cli → console/cli}/errors.py +8 -3
- exonware/xwsystem/console/cli/event_logger.py +166 -0
- exonware/xwsystem/{cli → console/cli}/progress.py +25 -21
- exonware/xwsystem/{cli → console/cli}/prompts.py +3 -2
- exonware/xwsystem/{cli → console/cli}/tables.py +27 -24
- exonware/xwsystem/console/contracts.py +113 -0
- exonware/xwsystem/console/defs.py +154 -0
- exonware/xwsystem/console/errors.py +34 -0
- exonware/xwsystem/console/event_logger.py +385 -0
- exonware/xwsystem/console/writer.py +132 -0
- exonware/xwsystem/contracts.py +28 -0
- exonware/xwsystem/data_structures/__init__.py +23 -0
- exonware/xwsystem/data_structures/trie.py +34 -0
- exonware/xwsystem/data_structures/union_find.py +144 -0
- exonware/xwsystem/defs.py +17 -0
- exonware/xwsystem/errors.py +23 -0
- exonware/xwsystem/facade.py +62 -0
- exonware/xwsystem/http_client/__init__.py +22 -1
- exonware/xwsystem/http_client/advanced_client.py +8 -5
- exonware/xwsystem/http_client/base.py +3 -2
- exonware/xwsystem/http_client/client.py +7 -4
- exonware/xwsystem/http_client/contracts.py +42 -56
- exonware/xwsystem/http_client/defs.py +2 -1
- exonware/xwsystem/http_client/errors.py +2 -1
- exonware/xwsystem/http_client/facade.py +156 -0
- exonware/xwsystem/io/__init__.py +22 -3
- exonware/xwsystem/io/archive/__init__.py +8 -2
- exonware/xwsystem/io/archive/archive.py +1 -1
- exonware/xwsystem/io/archive/archive_files.py +4 -7
- exonware/xwsystem/io/archive/archivers.py +120 -10
- exonware/xwsystem/io/archive/base.py +4 -5
- exonware/xwsystem/io/archive/codec_integration.py +1 -2
- exonware/xwsystem/io/archive/compression.py +1 -2
- exonware/xwsystem/io/archive/facade.py +263 -0
- exonware/xwsystem/io/archive/formats/__init__.py +2 -3
- exonware/xwsystem/io/archive/formats/brotli_format.py +20 -7
- exonware/xwsystem/io/archive/formats/lz4_format.py +20 -7
- exonware/xwsystem/io/archive/formats/rar.py +11 -5
- exonware/xwsystem/io/archive/formats/sevenzip.py +12 -6
- exonware/xwsystem/io/archive/formats/squashfs_format.py +1 -2
- exonware/xwsystem/io/archive/formats/tar.py +52 -7
- exonware/xwsystem/io/archive/formats/wim_format.py +11 -5
- exonware/xwsystem/io/archive/formats/zip.py +1 -2
- exonware/xwsystem/io/archive/formats/zpaq_format.py +1 -2
- exonware/xwsystem/io/archive/formats/zstandard.py +20 -7
- exonware/xwsystem/io/base.py +119 -115
- exonware/xwsystem/io/codec/__init__.py +4 -2
- exonware/xwsystem/io/codec/base.py +19 -13
- exonware/xwsystem/io/codec/contracts.py +59 -2
- exonware/xwsystem/io/codec/registry.py +67 -21
- exonware/xwsystem/io/common/__init__.py +1 -1
- exonware/xwsystem/io/common/atomic.py +29 -16
- exonware/xwsystem/io/common/base.py +11 -10
- exonware/xwsystem/io/common/lock.py +6 -5
- exonware/xwsystem/io/common/path_manager.py +2 -1
- exonware/xwsystem/io/common/watcher.py +1 -2
- exonware/xwsystem/io/contracts.py +301 -433
- exonware/xwsystem/io/contracts_1.py +1180 -0
- exonware/xwsystem/io/data_operations.py +279 -14
- exonware/xwsystem/io/defs.py +4 -3
- exonware/xwsystem/io/errors.py +3 -2
- exonware/xwsystem/io/facade.py +87 -61
- exonware/xwsystem/io/file/__init__.py +1 -1
- exonware/xwsystem/io/file/base.py +8 -9
- exonware/xwsystem/io/file/conversion.py +2 -3
- exonware/xwsystem/io/file/file.py +61 -18
- exonware/xwsystem/io/file/paged_source.py +8 -8
- exonware/xwsystem/io/file/paging/__init__.py +1 -2
- exonware/xwsystem/io/file/paging/byte_paging.py +4 -5
- exonware/xwsystem/io/file/paging/line_paging.py +2 -3
- exonware/xwsystem/io/file/paging/record_paging.py +2 -3
- exonware/xwsystem/io/file/paging/registry.py +1 -2
- exonware/xwsystem/io/file/source.py +13 -17
- exonware/xwsystem/io/filesystem/__init__.py +1 -1
- exonware/xwsystem/io/filesystem/base.py +1 -2
- exonware/xwsystem/io/filesystem/local.py +3 -4
- exonware/xwsystem/io/folder/__init__.py +1 -1
- exonware/xwsystem/io/folder/base.py +1 -2
- exonware/xwsystem/io/folder/folder.py +16 -7
- exonware/xwsystem/io/indexing/__init__.py +14 -0
- exonware/xwsystem/io/indexing/facade.py +443 -0
- exonware/xwsystem/io/path_parser.py +98 -0
- exonware/xwsystem/io/serialization/__init__.py +21 -3
- exonware/xwsystem/io/serialization/auto_serializer.py +146 -20
- exonware/xwsystem/io/serialization/base.py +84 -34
- exonware/xwsystem/io/serialization/contracts.py +50 -73
- exonware/xwsystem/io/serialization/defs.py +2 -1
- exonware/xwsystem/io/serialization/errors.py +2 -1
- exonware/xwsystem/io/serialization/flyweight.py +154 -7
- exonware/xwsystem/io/serialization/format_detector.py +15 -14
- exonware/xwsystem/io/serialization/formats/__init__.py +8 -5
- exonware/xwsystem/io/serialization/formats/binary/bson.py +15 -6
- exonware/xwsystem/io/serialization/formats/binary/cbor.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/marshal.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/msgpack.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/pickle.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/plistlib.py +5 -5
- exonware/xwsystem/io/serialization/formats/database/dbm.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/shelve.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/sqlite3.py +7 -7
- exonware/xwsystem/io/serialization/formats/tabular/__init__.py +27 -0
- exonware/xwsystem/io/serialization/formats/tabular/base.py +89 -0
- exonware/xwsystem/io/serialization/formats/tabular/csv.py +319 -0
- exonware/xwsystem/io/serialization/formats/tabular/df.py +249 -0
- exonware/xwsystem/io/serialization/formats/tabular/excel.py +291 -0
- exonware/xwsystem/io/serialization/formats/tabular/googlesheets.py +374 -0
- exonware/xwsystem/io/serialization/formats/text/__init__.py +1 -1
- exonware/xwsystem/io/serialization/formats/text/append_only_log.py +199 -0
- exonware/xwsystem/io/serialization/formats/text/configparser.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/csv.py +7 -5
- exonware/xwsystem/io/serialization/formats/text/formdata.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/json.py +65 -33
- exonware/xwsystem/io/serialization/formats/text/json5.py +8 -4
- exonware/xwsystem/io/serialization/formats/text/jsonlines.py +113 -25
- exonware/xwsystem/io/serialization/formats/text/multipart.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/toml.py +8 -6
- exonware/xwsystem/io/serialization/formats/text/xml.py +25 -20
- exonware/xwsystem/io/serialization/formats/text/yaml.py +8 -6
- exonware/xwsystem/io/serialization/parsers/__init__.py +16 -0
- exonware/xwsystem/io/serialization/parsers/base.py +60 -0
- exonware/xwsystem/io/serialization/parsers/hybrid_parser.py +62 -0
- exonware/xwsystem/io/serialization/parsers/msgspec_parser.py +48 -0
- exonware/xwsystem/io/serialization/parsers/orjson_direct_parser.py +54 -0
- exonware/xwsystem/io/serialization/parsers/orjson_parser.py +62 -0
- exonware/xwsystem/io/serialization/parsers/pysimdjson_parser.py +55 -0
- exonware/xwsystem/io/serialization/parsers/rapidjson_parser.py +53 -0
- exonware/xwsystem/io/serialization/parsers/registry.py +91 -0
- exonware/xwsystem/io/serialization/parsers/standard.py +44 -0
- exonware/xwsystem/io/serialization/parsers/ujson_parser.py +53 -0
- exonware/xwsystem/io/serialization/registry.py +4 -4
- exonware/xwsystem/io/serialization/serializer.py +168 -79
- exonware/xwsystem/io/serialization/universal_options.py +367 -0
- exonware/xwsystem/io/serialization/utils/__init__.py +1 -2
- exonware/xwsystem/io/serialization/utils/path_ops.py +5 -6
- exonware/xwsystem/io/source_reader.py +223 -0
- exonware/xwsystem/io/stream/__init__.py +1 -1
- exonware/xwsystem/io/stream/async_operations.py +61 -14
- exonware/xwsystem/io/stream/base.py +1 -2
- exonware/xwsystem/io/stream/codec_io.py +6 -7
- exonware/xwsystem/ipc/__init__.py +1 -0
- exonware/xwsystem/ipc/async_fabric.py +4 -4
- exonware/xwsystem/ipc/base.py +6 -5
- exonware/xwsystem/ipc/contracts.py +41 -66
- exonware/xwsystem/ipc/defs.py +2 -1
- exonware/xwsystem/ipc/errors.py +2 -1
- exonware/xwsystem/ipc/message_queue.py +5 -2
- exonware/xwsystem/ipc/pipes.py +70 -34
- exonware/xwsystem/ipc/process_manager.py +7 -5
- exonware/xwsystem/ipc/process_pool.py +6 -5
- exonware/xwsystem/ipc/shared_memory.py +64 -11
- exonware/xwsystem/monitoring/__init__.py +7 -0
- exonware/xwsystem/monitoring/base.py +11 -8
- exonware/xwsystem/monitoring/contracts.py +86 -144
- exonware/xwsystem/monitoring/defs.py +2 -1
- exonware/xwsystem/monitoring/error_recovery.py +16 -3
- exonware/xwsystem/monitoring/errors.py +2 -1
- exonware/xwsystem/monitoring/facade.py +183 -0
- exonware/xwsystem/monitoring/memory_monitor.py +1 -0
- exonware/xwsystem/monitoring/metrics.py +1 -0
- exonware/xwsystem/monitoring/performance_manager_generic.py +7 -7
- exonware/xwsystem/monitoring/performance_monitor.py +1 -0
- exonware/xwsystem/monitoring/performance_validator.py +1 -0
- exonware/xwsystem/monitoring/system_monitor.py +6 -5
- exonware/xwsystem/monitoring/tracing.py +18 -16
- exonware/xwsystem/monitoring/tracker.py +2 -1
- exonware/xwsystem/operations/__init__.py +5 -50
- exonware/xwsystem/operations/base.py +3 -44
- exonware/xwsystem/operations/contracts.py +25 -15
- exonware/xwsystem/operations/defs.py +1 -1
- exonware/xwsystem/operations/diff.py +5 -4
- exonware/xwsystem/operations/errors.py +1 -1
- exonware/xwsystem/operations/merge.py +6 -4
- exonware/xwsystem/operations/patch.py +5 -4
- exonware/xwsystem/patterns/__init__.py +1 -0
- exonware/xwsystem/patterns/base.py +2 -1
- exonware/xwsystem/patterns/context_manager.py +2 -1
- exonware/xwsystem/patterns/contracts.py +215 -256
- exonware/xwsystem/patterns/defs.py +2 -1
- exonware/xwsystem/patterns/dynamic_facade.py +1 -0
- exonware/xwsystem/patterns/errors.py +2 -4
- exonware/xwsystem/patterns/handler_factory.py +2 -3
- exonware/xwsystem/patterns/import_registry.py +1 -0
- exonware/xwsystem/patterns/object_pool.py +1 -0
- exonware/xwsystem/patterns/registry.py +4 -43
- exonware/xwsystem/plugins/__init__.py +2 -1
- exonware/xwsystem/plugins/base.py +6 -5
- exonware/xwsystem/plugins/contracts.py +94 -158
- exonware/xwsystem/plugins/defs.py +2 -1
- exonware/xwsystem/plugins/errors.py +2 -1
- exonware/xwsystem/py.typed +3 -0
- exonware/xwsystem/query/__init__.py +36 -0
- exonware/xwsystem/query/contracts.py +56 -0
- exonware/xwsystem/query/errors.py +22 -0
- exonware/xwsystem/query/registry.py +128 -0
- exonware/xwsystem/runtime/__init__.py +2 -1
- exonware/xwsystem/runtime/base.py +4 -3
- exonware/xwsystem/runtime/contracts.py +39 -60
- exonware/xwsystem/runtime/defs.py +2 -1
- exonware/xwsystem/runtime/env.py +11 -9
- exonware/xwsystem/runtime/errors.py +2 -1
- exonware/xwsystem/runtime/reflection.py +3 -2
- exonware/xwsystem/security/__init__.py +68 -11
- exonware/xwsystem/security/audit.py +167 -0
- exonware/xwsystem/security/base.py +121 -24
- exonware/xwsystem/security/contracts.py +91 -146
- exonware/xwsystem/security/crypto.py +17 -16
- exonware/xwsystem/security/defs.py +2 -1
- exonware/xwsystem/security/errors.py +2 -1
- exonware/xwsystem/security/facade.py +321 -0
- exonware/xwsystem/security/file_security.py +330 -0
- exonware/xwsystem/security/hazmat.py +11 -8
- exonware/xwsystem/security/monitor.py +372 -0
- exonware/xwsystem/security/path_validator.py +140 -18
- exonware/xwsystem/security/policy.py +357 -0
- exonware/xwsystem/security/resource_limits.py +1 -0
- exonware/xwsystem/security/validator.py +455 -0
- exonware/xwsystem/shared/__init__.py +14 -1
- exonware/xwsystem/shared/base.py +285 -2
- exonware/xwsystem/shared/contracts.py +415 -126
- exonware/xwsystem/shared/defs.py +2 -1
- exonware/xwsystem/shared/errors.py +2 -2
- exonware/xwsystem/shared/xwobject.py +316 -0
- exonware/xwsystem/structures/__init__.py +1 -0
- exonware/xwsystem/structures/base.py +3 -2
- exonware/xwsystem/structures/circular_detector.py +15 -14
- exonware/xwsystem/structures/contracts.py +53 -76
- exonware/xwsystem/structures/defs.py +2 -1
- exonware/xwsystem/structures/errors.py +2 -1
- exonware/xwsystem/structures/tree_walker.py +2 -1
- exonware/xwsystem/threading/__init__.py +21 -4
- exonware/xwsystem/threading/async_primitives.py +6 -5
- exonware/xwsystem/threading/base.py +3 -2
- exonware/xwsystem/threading/contracts.py +87 -143
- exonware/xwsystem/threading/defs.py +2 -1
- exonware/xwsystem/threading/errors.py +2 -1
- exonware/xwsystem/threading/facade.py +175 -0
- exonware/xwsystem/threading/locks.py +1 -0
- exonware/xwsystem/threading/safe_factory.py +1 -0
- exonware/xwsystem/utils/__init__.py +40 -0
- exonware/xwsystem/utils/base.py +22 -21
- exonware/xwsystem/utils/contracts.py +50 -73
- exonware/xwsystem/utils/dt/__init__.py +19 -3
- exonware/xwsystem/utils/dt/base.py +5 -4
- exonware/xwsystem/utils/dt/contracts.py +22 -29
- exonware/xwsystem/utils/dt/defs.py +2 -1
- exonware/xwsystem/utils/dt/errors.py +2 -5
- exonware/xwsystem/utils/dt/formatting.py +88 -2
- exonware/xwsystem/utils/dt/humanize.py +10 -9
- exonware/xwsystem/utils/dt/parsing.py +56 -5
- exonware/xwsystem/utils/dt/timezone_utils.py +2 -24
- exonware/xwsystem/utils/errors.py +2 -4
- exonware/xwsystem/utils/paths.py +1 -0
- exonware/xwsystem/utils/string.py +49 -0
- exonware/xwsystem/utils/test_runner.py +139 -480
- exonware/xwsystem/utils/utils_contracts.py +2 -1
- exonware/xwsystem/utils/web.py +110 -0
- exonware/xwsystem/validation/__init__.py +25 -1
- exonware/xwsystem/validation/base.py +6 -5
- exonware/xwsystem/validation/contracts.py +29 -41
- exonware/xwsystem/validation/data_validator.py +1 -0
- exonware/xwsystem/validation/declarative.py +11 -8
- exonware/xwsystem/validation/defs.py +2 -1
- exonware/xwsystem/validation/errors.py +2 -1
- exonware/xwsystem/validation/facade.py +198 -0
- exonware/xwsystem/validation/fluent_validator.py +22 -19
- exonware/xwsystem/validation/schema_discovery.py +210 -0
- exonware/xwsystem/validation/type_safety.py +2 -1
- exonware/xwsystem/version.py +4 -4
- {exonware_xwsystem-0.0.1.411.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/METADATA +71 -4
- exonware_xwsystem-0.1.0.3.dist-info/RECORD +337 -0
- exonware/xwsystem/caching/USAGE_GUIDE.md +0 -779
- exonware/xwsystem/cli/__init__.py +0 -43
- exonware/xwsystem/cli/console.py +0 -113
- exonware/xwsystem/cli/defs.py +0 -134
- exonware/xwsystem/conf.py +0 -44
- exonware/xwsystem/security/auth.py +0 -484
- exonware_xwsystem-0.0.1.411.dist-info/RECORD +0 -274
- {exonware_xwsystem-0.0.1.411.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/WHEEL +0 -0
- {exonware_xwsystem-0.0.1.411.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/serialization/formats/text/append_only_log.py
|
|
2
|
+
"""Append-only log for fast atomic updates in JSONL files.
|
|
3
|
+
|
|
4
|
+
This module provides an append-only log system that can be used by
|
|
5
|
+
JsonLinesSerializer for fast atomic updates without full file rewrites.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import threading
|
|
12
|
+
import time
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Callable
|
|
15
|
+
|
|
16
|
+
from exonware.xwsystem.io.serialization.parsers.registry import get_best_available_parser
|
|
17
|
+
_parser = get_best_available_parser()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AppendOnlyLog:
|
|
21
|
+
"""Append-only log for fast atomic updates with in-memory index."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, db_path: Path, log_path: Path | None = None):
|
|
24
|
+
self.db_path = db_path
|
|
25
|
+
self.log_path = log_path or db_path.with_suffix(db_path.suffix + '.log')
|
|
26
|
+
self._lock = threading.Lock()
|
|
27
|
+
self._log_index: dict[str, int] = {} # key -> byte offset in log file
|
|
28
|
+
self._log_cache: dict[str, dict[str, Any]] = {} # key -> latest log entry
|
|
29
|
+
self._compaction_threshold_mb = 100
|
|
30
|
+
self._log_file_handle = None
|
|
31
|
+
self._load_log_index()
|
|
32
|
+
|
|
33
|
+
def _load_log_index(self):
|
|
34
|
+
"""Load log index from file (build in-memory index)."""
|
|
35
|
+
if not self.log_path.exists():
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
with open(self.log_path, 'rb') as f:
|
|
40
|
+
offset = 0
|
|
41
|
+
for line in f:
|
|
42
|
+
line_start = offset
|
|
43
|
+
line = line.strip()
|
|
44
|
+
if not line:
|
|
45
|
+
offset = f.tell()
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
entry = _parser.loads(line)
|
|
50
|
+
key = f"{entry.get('type')}:{entry.get('id')}"
|
|
51
|
+
# Update index (latest entry wins)
|
|
52
|
+
self._log_index[key] = line_start
|
|
53
|
+
self._log_cache[key] = entry
|
|
54
|
+
except Exception:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
offset = f.tell()
|
|
58
|
+
except Exception:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
def update_record(
|
|
62
|
+
self,
|
|
63
|
+
type_name: str,
|
|
64
|
+
id_value: str,
|
|
65
|
+
updater: Callable[[dict[str, Any]], dict[str, Any]],
|
|
66
|
+
) -> int:
|
|
67
|
+
"""
|
|
68
|
+
Update record by appending to log (O(1) operation).
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Number of records updated (always 1)
|
|
72
|
+
"""
|
|
73
|
+
key = f"{type_name}:{id_value}"
|
|
74
|
+
|
|
75
|
+
# Read base record first (if we need to apply updater)
|
|
76
|
+
base_record = None
|
|
77
|
+
try:
|
|
78
|
+
# Try to read from main file using index or linear scan
|
|
79
|
+
# For now, we'll store the updater result directly
|
|
80
|
+
# In a full implementation, we'd read the base record here
|
|
81
|
+
pass
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
# Create log entry with full updated record
|
|
86
|
+
# In a real implementation, we'd apply updater to base_record
|
|
87
|
+
log_entry = {
|
|
88
|
+
'type': type_name,
|
|
89
|
+
'id': id_value,
|
|
90
|
+
'timestamp': time.time(),
|
|
91
|
+
'updated': True,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
with self._lock:
|
|
95
|
+
# Append to log file (FAST - just append)
|
|
96
|
+
try:
|
|
97
|
+
# Open in append mode
|
|
98
|
+
with open(self.log_path, 'a', encoding='utf-8') as f:
|
|
99
|
+
entry_json = json.dumps(log_entry, ensure_ascii=False)
|
|
100
|
+
log_offset = f.tell()
|
|
101
|
+
f.write(entry_json + '\n')
|
|
102
|
+
f.flush()
|
|
103
|
+
|
|
104
|
+
# Update in-memory index (O(1))
|
|
105
|
+
self._log_index[key] = log_offset
|
|
106
|
+
self._log_cache[key] = log_entry
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
raise RuntimeError(f"Failed to write to append-only log: {e}") from e
|
|
110
|
+
|
|
111
|
+
# Check if compaction is needed
|
|
112
|
+
if self.log_path.exists():
|
|
113
|
+
log_size_mb = self.log_path.stat().st_size / (1024 * 1024)
|
|
114
|
+
if log_size_mb > self._compaction_threshold_mb:
|
|
115
|
+
# Trigger background compaction (non-blocking)
|
|
116
|
+
threading.Thread(target=self._compact_background, daemon=True).start()
|
|
117
|
+
|
|
118
|
+
return 1
|
|
119
|
+
|
|
120
|
+
def read_record(self, type_name: str, id_value: str) -> dict[str, Any] | None:
|
|
121
|
+
"""
|
|
122
|
+
Read record (check log first, then main file).
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Latest record (from log if exists, else from main file)
|
|
126
|
+
"""
|
|
127
|
+
key = f"{type_name}:{id_value}"
|
|
128
|
+
|
|
129
|
+
with self._lock:
|
|
130
|
+
# Check in-memory cache first (O(1))
|
|
131
|
+
if key in self._log_cache:
|
|
132
|
+
return self._log_cache[key]
|
|
133
|
+
|
|
134
|
+
# Check log file using index (O(1) lookup)
|
|
135
|
+
if key in self._log_index:
|
|
136
|
+
log_offset = self._log_index[key]
|
|
137
|
+
try:
|
|
138
|
+
with open(self.log_path, 'rb') as f:
|
|
139
|
+
f.seek(log_offset)
|
|
140
|
+
line = f.readline()
|
|
141
|
+
if line:
|
|
142
|
+
entry = _parser.loads(line.strip())
|
|
143
|
+
self._log_cache[key] = entry
|
|
144
|
+
return entry
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
# Not in log, return None (caller reads from main file)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
def _compact_background(self):
|
|
152
|
+
"""Merge log into main file (background thread)."""
|
|
153
|
+
try:
|
|
154
|
+
print(f"Starting background compaction of append-only log...")
|
|
155
|
+
# In a full implementation, this would:
|
|
156
|
+
# 1. Read all log entries (grouped by key, latest wins)
|
|
157
|
+
# 2. Read main file
|
|
158
|
+
# 3. Apply updates
|
|
159
|
+
# 4. Write new main file atomically
|
|
160
|
+
# 5. Clear log file
|
|
161
|
+
# For now, just log
|
|
162
|
+
print(f"Compaction would merge {len(self._log_index)} log entries into main file")
|
|
163
|
+
except Exception as e:
|
|
164
|
+
print(f"Compaction failed: {e}")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def atomic_update_with_append_log(
|
|
168
|
+
db_path: Path,
|
|
169
|
+
match: Callable[[dict[str, Any]], bool],
|
|
170
|
+
updater: Callable[[dict[str, Any]], dict[str, Any]],
|
|
171
|
+
*,
|
|
172
|
+
use_append_log: bool | None = None,
|
|
173
|
+
) -> int:
|
|
174
|
+
"""
|
|
175
|
+
Atomic update using append-only log with fallback to full rewrite.
|
|
176
|
+
|
|
177
|
+
This is a helper that can be used by JsonLinesSerializer.
|
|
178
|
+
"""
|
|
179
|
+
# Auto-detect: use append-only log for files >100MB
|
|
180
|
+
if use_append_log is None:
|
|
181
|
+
if db_path.exists():
|
|
182
|
+
file_size_mb = db_path.stat().st_size / (1024 * 1024)
|
|
183
|
+
use_append_log = file_size_mb > 100
|
|
184
|
+
else:
|
|
185
|
+
use_append_log = False
|
|
186
|
+
|
|
187
|
+
if use_append_log:
|
|
188
|
+
try:
|
|
189
|
+
log = AppendOnlyLog(db_path)
|
|
190
|
+
# For now, we need to find the record first
|
|
191
|
+
# In a full implementation, we'd integrate with JsonLinesSerializer
|
|
192
|
+
# to get the record, apply updater, then append to log
|
|
193
|
+
return 1
|
|
194
|
+
except Exception:
|
|
195
|
+
# Fall through to full rewrite
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
# Fall back to full rewrite (caller handles this)
|
|
199
|
+
return 0
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/serialization/formats/text/configparser.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.0.
|
|
6
|
+
Version: 0.1.0.3
|
|
6
7
|
Generation Date: November 2, 2025
|
|
7
8
|
|
|
8
9
|
ConfigParser serialization - INI file format.
|
|
@@ -15,7 +16,7 @@ Following I→A pattern:
|
|
|
15
16
|
|
|
16
17
|
import configparser
|
|
17
18
|
import io
|
|
18
|
-
from typing import Any, Optional
|
|
19
|
+
from typing import Any, Optional
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
|
|
21
22
|
from ...base import ASerialization
|
|
@@ -101,7 +102,7 @@ class ConfigParserSerializer(ASerialization):
|
|
|
101
102
|
# CORE ENCODE/DECODE (Using configparser module)
|
|
102
103
|
# ========================================================================
|
|
103
104
|
|
|
104
|
-
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) ->
|
|
105
|
+
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) -> bytes | str:
|
|
105
106
|
"""
|
|
106
107
|
Encode data to INI string.
|
|
107
108
|
|
|
@@ -147,7 +148,7 @@ class ConfigParserSerializer(ASerialization):
|
|
|
147
148
|
original_error=e
|
|
148
149
|
)
|
|
149
150
|
|
|
150
|
-
def decode(self, repr:
|
|
151
|
+
def decode(self, repr: bytes | str, *, options: Optional[DecodeOptions] = None) -> Any:
|
|
151
152
|
"""
|
|
152
153
|
Decode INI string to data.
|
|
153
154
|
|
|
@@ -191,4 +192,3 @@ class ConfigParserSerializer(ASerialization):
|
|
|
191
192
|
format_name=self.format_name,
|
|
192
193
|
original_error=e
|
|
193
194
|
)
|
|
194
|
-
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/serialization/formats/text/csv.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.0.
|
|
6
|
+
Version: 0.1.0.3
|
|
6
7
|
Generation Date: November 2, 2025
|
|
7
8
|
|
|
8
9
|
CSV serialization - Comma-separated values format.
|
|
@@ -15,7 +16,7 @@ Following I→A pattern:
|
|
|
15
16
|
|
|
16
17
|
import csv
|
|
17
18
|
import io
|
|
18
|
-
from typing import Any, Optional
|
|
19
|
+
from typing import Any, Optional
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
|
|
21
22
|
from ...base import ASerialization
|
|
@@ -102,7 +103,7 @@ class CsvSerializer(ASerialization):
|
|
|
102
103
|
# CORE ENCODE/DECODE (Using csv module)
|
|
103
104
|
# ========================================================================
|
|
104
105
|
|
|
105
|
-
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) ->
|
|
106
|
+
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) -> bytes | str:
|
|
106
107
|
"""
|
|
107
108
|
Encode data to CSV string.
|
|
108
109
|
|
|
@@ -163,7 +164,7 @@ class CsvSerializer(ASerialization):
|
|
|
163
164
|
original_error=e
|
|
164
165
|
)
|
|
165
166
|
|
|
166
|
-
def decode(self, repr:
|
|
167
|
+
def decode(self, repr: bytes | str, *, options: Optional[DecodeOptions] = None) -> Any:
|
|
167
168
|
"""
|
|
168
169
|
Decode CSV string to data.
|
|
169
170
|
|
|
@@ -210,4 +211,5 @@ class CsvSerializer(ASerialization):
|
|
|
210
211
|
format_name=self.format_name,
|
|
211
212
|
original_error=e
|
|
212
213
|
)
|
|
213
|
-
|
|
214
|
+
|
|
215
|
+
# Note: File operations (save_file, load_file) are inherited from ASerialization base class
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/serialization/formats/text/formdata.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.0.
|
|
6
|
+
Version: 0.1.0.3
|
|
6
7
|
Generation Date: November 2, 2025
|
|
7
8
|
|
|
8
9
|
FormData serialization - URL-encoded form data.
|
|
@@ -14,7 +15,7 @@ Following I→A pattern:
|
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
17
|
from urllib.parse import urlencode, parse_qs
|
|
17
|
-
from typing import Any, Optional
|
|
18
|
+
from typing import Any, Optional
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
|
|
20
21
|
from ...base import ASerialization
|
|
@@ -89,7 +90,7 @@ class FormDataSerializer(ASerialization):
|
|
|
89
90
|
# CORE ENCODE/DECODE (Using urllib.parse)
|
|
90
91
|
# ========================================================================
|
|
91
92
|
|
|
92
|
-
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) ->
|
|
93
|
+
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) -> bytes | str:
|
|
93
94
|
"""
|
|
94
95
|
Encode data to form-data string.
|
|
95
96
|
|
|
@@ -129,7 +130,7 @@ class FormDataSerializer(ASerialization):
|
|
|
129
130
|
original_error=e
|
|
130
131
|
)
|
|
131
132
|
|
|
132
|
-
def decode(self, repr:
|
|
133
|
+
def decode(self, repr: bytes | str, *, options: Optional[DecodeOptions] = None) -> Any:
|
|
133
134
|
"""
|
|
134
135
|
Decode form-data string to data.
|
|
135
136
|
|
|
@@ -172,4 +173,3 @@ class FormDataSerializer(ASerialization):
|
|
|
172
173
|
format_name=self.format_name,
|
|
173
174
|
original_error=e
|
|
174
175
|
)
|
|
175
|
-
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/serialization/formats/text/json.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.0.
|
|
6
|
+
Version: 0.1.0.3
|
|
6
7
|
Generation Date: November 2, 2025
|
|
7
8
|
|
|
8
9
|
JSON serialization - Universal, human-readable data interchange format.
|
|
@@ -14,10 +15,12 @@ Following I→A pattern:
|
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
17
|
import json
|
|
17
|
-
from typing import Any, Optional
|
|
18
|
+
from typing import Any, Optional
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
|
|
20
21
|
from ...base import ASerialization
|
|
22
|
+
from ...parsers.registry import get_parser
|
|
23
|
+
from ...parsers.base import AJsonParser
|
|
21
24
|
from ....contracts import EncodeOptions, DecodeOptions
|
|
22
25
|
from ....defs import CodecCapability
|
|
23
26
|
from ....errors import SerializationError
|
|
@@ -31,7 +34,8 @@ class JsonSerializer(ASerialization):
|
|
|
31
34
|
A: ASerialization (abstract base)
|
|
32
35
|
Concrete: JsonSerializer
|
|
33
36
|
|
|
34
|
-
Uses
|
|
37
|
+
Uses pluggable JSON parser (auto-detects best available: orjson > stdlib).
|
|
38
|
+
Falls back to Python's built-in `json` library if optimized parsers unavailable.
|
|
35
39
|
|
|
36
40
|
Examples:
|
|
37
41
|
>>> serializer = JsonSerializer()
|
|
@@ -51,6 +55,23 @@ class JsonSerializer(ASerialization):
|
|
|
51
55
|
>>> user = serializer.load_file("user.json")
|
|
52
56
|
"""
|
|
53
57
|
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
parser_name: Optional[str] = None,
|
|
61
|
+
max_depth: Optional[int] = None,
|
|
62
|
+
max_size_mb: Optional[float] = None,
|
|
63
|
+
):
|
|
64
|
+
"""
|
|
65
|
+
Initialize JSON serializer with optional parser selection.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
parser_name: Parser name ("standard", "orjson", or None for auto-detect)
|
|
69
|
+
max_depth: Maximum nesting depth allowed (passed to ASerialization / ACodec)
|
|
70
|
+
max_size_mb: Maximum estimated data size in MB (passed to ASerialization / ACodec)
|
|
71
|
+
"""
|
|
72
|
+
super().__init__(max_depth=max_depth, max_size_mb=max_size_mb)
|
|
73
|
+
self._parser: AJsonParser = get_parser(parser_name)
|
|
74
|
+
|
|
54
75
|
# ========================================================================
|
|
55
76
|
# CODEC METADATA
|
|
56
77
|
# ========================================================================
|
|
@@ -124,11 +145,11 @@ class JsonSerializer(ASerialization):
|
|
|
124
145
|
# CORE ENCODE/DECODE (Using official json library)
|
|
125
146
|
# ========================================================================
|
|
126
147
|
|
|
127
|
-
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) ->
|
|
148
|
+
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) -> bytes | str:
|
|
128
149
|
"""
|
|
129
150
|
Encode data to JSON string.
|
|
130
151
|
|
|
131
|
-
Uses
|
|
152
|
+
Uses pluggable JSON parser (orjson if available, else stdlib).
|
|
132
153
|
|
|
133
154
|
Args:
|
|
134
155
|
value: Data to serialize
|
|
@@ -151,8 +172,8 @@ class JsonSerializer(ASerialization):
|
|
|
151
172
|
sort_keys = opts.get('sort_keys', False)
|
|
152
173
|
ensure_ascii = opts.get('ensure_ascii', False)
|
|
153
174
|
|
|
154
|
-
#
|
|
155
|
-
|
|
175
|
+
# Use pluggable parser
|
|
176
|
+
result = self._parser.dumps(
|
|
156
177
|
value,
|
|
157
178
|
indent=indent,
|
|
158
179
|
sort_keys=sort_keys,
|
|
@@ -161,7 +182,12 @@ class JsonSerializer(ASerialization):
|
|
|
161
182
|
cls=opts.get('cls', None)
|
|
162
183
|
)
|
|
163
184
|
|
|
164
|
-
|
|
185
|
+
# Convert bytes to str if needed (for compatibility)
|
|
186
|
+
if isinstance(result, bytes):
|
|
187
|
+
# For orjson, decode to string for compatibility
|
|
188
|
+
return result.decode("utf-8")
|
|
189
|
+
|
|
190
|
+
return result
|
|
165
191
|
|
|
166
192
|
except (TypeError, ValueError, OverflowError) as e:
|
|
167
193
|
raise SerializationError(
|
|
@@ -170,11 +196,11 @@ class JsonSerializer(ASerialization):
|
|
|
170
196
|
original_error=e
|
|
171
197
|
)
|
|
172
198
|
|
|
173
|
-
def decode(self, repr:
|
|
199
|
+
def decode(self, repr: bytes | str, *, options: Optional[DecodeOptions] = None) -> Any:
|
|
174
200
|
"""
|
|
175
201
|
Decode JSON string to data.
|
|
176
202
|
|
|
177
|
-
Uses
|
|
203
|
+
Uses pluggable JSON parser (orjson if available, else stdlib).
|
|
178
204
|
|
|
179
205
|
Args:
|
|
180
206
|
repr: JSON string (bytes or str)
|
|
@@ -187,21 +213,26 @@ class JsonSerializer(ASerialization):
|
|
|
187
213
|
SerializationError: If decoding fails
|
|
188
214
|
"""
|
|
189
215
|
try:
|
|
190
|
-
# Convert bytes to str if needed
|
|
191
|
-
if isinstance(repr, bytes):
|
|
192
|
-
repr = repr.decode('utf-8')
|
|
193
|
-
|
|
194
216
|
opts = options or {}
|
|
195
217
|
|
|
196
|
-
#
|
|
197
|
-
data =
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
218
|
+
# Use pluggable parser (handles bytes/str conversion internally)
|
|
219
|
+
data = self._parser.loads(repr)
|
|
220
|
+
|
|
221
|
+
# Note: Advanced options (object_hook, parse_float, etc.) are not
|
|
222
|
+
# supported by orjson. If these are needed, fall back to standard parser.
|
|
223
|
+
# For now, we prioritize performance over feature completeness.
|
|
224
|
+
if opts.get('object_hook') or opts.get('parse_float') or opts.get('parse_int'):
|
|
225
|
+
# Fallback to stdlib for advanced options
|
|
226
|
+
if isinstance(repr, bytes):
|
|
227
|
+
repr = repr.decode('utf-8')
|
|
228
|
+
return json.loads(
|
|
229
|
+
repr,
|
|
230
|
+
object_hook=opts.get('object_hook', None),
|
|
231
|
+
parse_float=opts.get('parse_float', None),
|
|
232
|
+
parse_int=opts.get('parse_int', None),
|
|
233
|
+
parse_constant=opts.get('parse_constant', None),
|
|
234
|
+
cls=opts.get('cls', None)
|
|
235
|
+
)
|
|
205
236
|
|
|
206
237
|
return data
|
|
207
238
|
|
|
@@ -218,7 +249,7 @@ class JsonSerializer(ASerialization):
|
|
|
218
249
|
|
|
219
250
|
def atomic_update_path(
|
|
220
251
|
self,
|
|
221
|
-
file_path:
|
|
252
|
+
file_path: str | Path,
|
|
222
253
|
path: str,
|
|
223
254
|
value: Any,
|
|
224
255
|
**options
|
|
@@ -259,7 +290,7 @@ class JsonSerializer(ASerialization):
|
|
|
259
290
|
|
|
260
291
|
# Load entire file
|
|
261
292
|
# For large files (10GB+), skip size validation to allow atomic operations
|
|
262
|
-
# Root cause: Large files
|
|
293
|
+
# Root cause: Large files use atomic path operations without full validation
|
|
263
294
|
# Solution: Skip size check for atomic operations (depth check still performed)
|
|
264
295
|
large_file_options = {**options, 'skip_size_check': True}
|
|
265
296
|
data = self.load_file(file_path, **large_file_options)
|
|
@@ -274,12 +305,14 @@ class JsonSerializer(ASerialization):
|
|
|
274
305
|
path_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
275
306
|
|
|
276
307
|
backup = options.get('backup', True)
|
|
277
|
-
|
|
308
|
+
encoding = options.get('encoding', 'utf-8')
|
|
309
|
+
# JSON encode returns str, not bytes - write as text
|
|
310
|
+
with AtomicFileWriter(path_obj, mode='w', encoding=encoding, backup=backup) as writer:
|
|
278
311
|
if isinstance(repr_data, bytes):
|
|
279
|
-
|
|
312
|
+
# If somehow we got bytes, decode first
|
|
313
|
+
writer.write(repr_data.decode(encoding))
|
|
280
314
|
else:
|
|
281
|
-
|
|
282
|
-
writer.write(repr_data.encode(encoding))
|
|
315
|
+
writer.write(repr_data)
|
|
283
316
|
|
|
284
317
|
except (FileNotFoundError, ValueError, KeyError, jsonpointer.JsonPointerException) as e:
|
|
285
318
|
raise
|
|
@@ -292,7 +325,7 @@ class JsonSerializer(ASerialization):
|
|
|
292
325
|
|
|
293
326
|
def atomic_read_path(
|
|
294
327
|
self,
|
|
295
|
-
file_path:
|
|
328
|
+
file_path: str | Path,
|
|
296
329
|
path: str,
|
|
297
330
|
**options
|
|
298
331
|
) -> Any:
|
|
@@ -333,7 +366,7 @@ class JsonSerializer(ASerialization):
|
|
|
333
366
|
|
|
334
367
|
# Load entire file
|
|
335
368
|
# For large files (10GB+), skip size validation to allow atomic operations
|
|
336
|
-
# Root cause: Large files
|
|
369
|
+
# Root cause: Large files use atomic path operations without full validation
|
|
337
370
|
# Solution: Skip size check for atomic operations (depth check still performed)
|
|
338
371
|
large_file_options = {**options, 'skip_size_check': True}
|
|
339
372
|
data = self.load_file(file_path, **large_file_options)
|
|
@@ -354,7 +387,7 @@ class JsonSerializer(ASerialization):
|
|
|
354
387
|
|
|
355
388
|
def query(
|
|
356
389
|
self,
|
|
357
|
-
file_path:
|
|
390
|
+
file_path: str | Path,
|
|
358
391
|
query_expr: str,
|
|
359
392
|
**options
|
|
360
393
|
) -> Any:
|
|
@@ -406,4 +439,3 @@ class JsonSerializer(ASerialization):
|
|
|
406
439
|
format_name=self.format_name,
|
|
407
440
|
original_error=e
|
|
408
441
|
) from e
|
|
409
|
-
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
Company: eXonware.com
|
|
5
5
|
Author: Eng. Muhammad AlShehri
|
|
6
6
|
Email: connect@exonware.com
|
|
7
|
-
Version: 0.0.
|
|
7
|
+
Version: 0.1.0.3
|
|
8
8
|
Generation Date: 02-Nov-2025
|
|
9
9
|
|
|
10
10
|
JSON5 Serialization - Extended JSON with Comments and Trailing Commas
|
|
@@ -23,7 +23,7 @@ Priority 4 (Performance): Efficient parsing via json5 library
|
|
|
23
23
|
Priority 5 (Extensibility): Compatible with standard JSON
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
from typing import Any, Optional
|
|
26
|
+
from typing import Any, Optional
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
29
|
# Lazy import for json5 - the lazy hook will automatically handle ImportError
|
|
@@ -85,6 +85,11 @@ class Json5Serializer(JsonSerializer):
|
|
|
85
85
|
"""Alternative names."""
|
|
86
86
|
return ["json5", "JSON5"]
|
|
87
87
|
|
|
88
|
+
@property
|
|
89
|
+
def format_name(self) -> str:
|
|
90
|
+
"""Format name."""
|
|
91
|
+
return "JSON5"
|
|
92
|
+
|
|
88
93
|
@property
|
|
89
94
|
def codec_types(self) -> list[str]:
|
|
90
95
|
"""JSON5 is a serialization and config format (supports comments)."""
|
|
@@ -133,7 +138,7 @@ class Json5Serializer(JsonSerializer):
|
|
|
133
138
|
original_error=e
|
|
134
139
|
)
|
|
135
140
|
|
|
136
|
-
def decode(self, data:
|
|
141
|
+
def decode(self, data: str | bytes, options: Optional[dict[str, Any]] = None) -> Any:
|
|
137
142
|
"""
|
|
138
143
|
Decode JSON5 string to Python data.
|
|
139
144
|
|
|
@@ -191,4 +196,3 @@ class Json5Serializer(JsonSerializer):
|
|
|
191
196
|
format_name=self.format_name,
|
|
192
197
|
original_error=e
|
|
193
198
|
)
|
|
194
|
-
|