tab-cli 0.1.6__tar.gz → 0.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.gitignore +0 -1
- {tab_cli-0.1.6 → tab_cli-0.1.8}/AGENTS.md +6 -2
- tab_cli-0.1.8/CHANGELOG.md +34 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/Makefile +3 -3
- tab_cli-0.1.8/PKG-INFO +121 -0
- tab_cli-0.1.8/README.md +94 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/docs/cli-ref.md +2 -2
- {tab_cli-0.1.6 → tab_cli-0.1.8}/docs/configuration.md +8 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/pyproject.toml +3 -3
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/__init__.py +1 -1
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/cli.py +156 -34
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/config.py +24 -2
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/formats/avro.py +7 -2
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/formats/base.py +7 -2
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/formats/csv.py +7 -2
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/formats/jsonl.py +7 -2
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/formats/parquet.py +16 -7
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/handlers/__init__.py +28 -4
- tab_cli-0.1.8/src/tab_cli/handlers/base.py +373 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/storage/__init__.py +14 -4
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/storage/aws.py +14 -15
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/storage/az.py +11 -8
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/storage/base.py +4 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/storage/fsspec.py +22 -9
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/storage/gcloud.py +17 -14
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/storage/local.py +12 -2
- tab_cli-0.1.8/tests/conftest.py +12 -0
- tab_cli-0.1.8/tests/test_cat.py +150 -0
- tab_cli-0.1.8/tests/test_config.py +181 -0
- tab_cli-0.1.8/tests/test_stdin.py +66 -0
- tab_cli-0.1.8/tests/test_storage.py +83 -0
- tab_cli-0.1.8/tests/test_summary.py +161 -0
- tab_cli-0.1.8/tests/test_view.py +192 -0
- tab_cli-0.1.8/uv.lock +2688 -0
- tab_cli-0.1.6/CHANGELOG.md +0 -18
- tab_cli-0.1.6/PKG-INFO +0 -37
- tab_cli-0.1.6/README.md +0 -10
- tab_cli-0.1.6/src/tab_cli/handlers/base.py +0 -259
- tab_cli-0.1.6/tests/test_cli.py +0 -357
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/.gitignore +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/282ad8cf3324b2679a7d460c0fc324adfa21dcfad2f197ac6991b98ec91f98495bb3ddb2cba36ce5dfa28a52063a373bb03f5d2e34f6e0c7b6b81b3046a4d7d0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/891d7ed26f62b0f8757b081e9d76840636877f1613ee17b7695fe2ee8640258c56e01291934e3797728bc2300a1b5f41e3f3ef81ab532c0e5ab475bb9b6f097a +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/8926397375b8328137652c18cd4371808b214cb26864d77dd33c8eea895e10e62c297064c3d4c703d58ee11dee81bbf9851525e3a0c33151f54c164b8a4343b0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/89d29d938e64b1295e138faedfa5df6f6729a67e5ba8e0c3fdd1c9266e0f59a4bc35b8a21e14225072317879af3323fa0457e01d9b08608770bc23957feec6c0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/95fc2854c0fa2528b03e2bcabc7612f56f0bcb3d6f54a06293e21d1887885a449907b5b320a1a94bac712bf56fdad0a4184b17047c1fc179dfa83acab3f70d21 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/9a58ceae46de4649375b6b880b8500c85d34c8e9bc650dc4c993afb2fa8d45c4180331821c6226a0fb1475b04a0b9a849502647ef9c4dcac74769eb501c885fa +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/a19ae062208aa20b7310705af3d26ef53095a9dabfad080883cc7a32e98687063179db95cb2c71ef9064801c59fa46d261f172d83e83f96a39c274387b59dca4 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/d1169a8d10067493e42752c2a7615ff27f55bd90c38b91feef918958e29a2239ee289a0cde8385441d1f0fe9af1fb634fa6d56438b9f33ec382e81fa59d70b54 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/e171e5c6039c050c2368584a537d36df221b1f9f23d285c0399b95b14608d67006229823e6831bff7d8b0c2f9e86ebaec9c6811461119195f430ade055073fed +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/op_links/ee118dd19aac4e1cb354f83a37dadca70bc5f086cc8d36cd0059b222bf8c7250824401ff681518d0120f8db1f96f0025c464dcfc3b1ee28777e8c76325760134 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/282ad8cf3324b2679a7d460c0fc324adfa21dcfad2f197ac6991b98ec91f98495bb3ddb2cba36ce5dfa28a52063a373bb03f5d2e34f6e0c7b6b81b3046a4d7d0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/891d7ed26f62b0f8757b081e9d76840636877f1613ee17b7695fe2ee8640258c56e01291934e3797728bc2300a1b5f41e3f3ef81ab532c0e5ab475bb9b6f097a +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/8926397375b8328137652c18cd4371808b214cb26864d77dd33c8eea895e10e62c297064c3d4c703d58ee11dee81bbf9851525e3a0c33151f54c164b8a4343b0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/89d29d938e64b1295e138faedfa5df6f6729a67e5ba8e0c3fdd1c9266e0f59a4bc35b8a21e14225072317879af3323fa0457e01d9b08608770bc23957feec6c0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/95fc2854c0fa2528b03e2bcabc7612f56f0bcb3d6f54a06293e21d1887885a449907b5b320a1a94bac712bf56fdad0a4184b17047c1fc179dfa83acab3f70d21 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/9a58ceae46de4649375b6b880b8500c85d34c8e9bc650dc4c993afb2fa8d45c4180331821c6226a0fb1475b04a0b9a849502647ef9c4dcac74769eb501c885fa +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/a19ae062208aa20b7310705af3d26ef53095a9dabfad080883cc7a32e98687063179db95cb2c71ef9064801c59fa46d261f172d83e83f96a39c274387b59dca4 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/d1169a8d10067493e42752c2a7615ff27f55bd90c38b91feef918958e29a2239ee289a0cde8385441d1f0fe9af1fb634fa6d56438b9f33ec382e81fa59d70b54 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/e171e5c6039c050c2368584a537d36df221b1f9f23d285c0399b95b14608d67006229823e6831bff7d8b0c2f9e86ebaec9c6811461119195f430ade055073fed +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/operations/ee118dd19aac4e1cb354f83a37dadca70bc5f086cc8d36cd0059b222bf8c7250824401ff681518d0120f8db1f96f0025c464dcfc3b1ee28777e8c76325760134 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/1029dd1c1f4430d8a667a0e48d0b817652c7ddca6f5ff56cac1755e5bb0c1cb7586935941c9b36f26cab05c0effe6154d073bd1dffbe37f22fed0a6e7d79201f +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/3228b9bef9d8374b5532b40df2da8be3bfc86de713bdad7fe620977ffa7c56db83928678caa792bf0d328db607028e045a9e41423ef7501e5b550651c3815ffe +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/424c9a53f5458b328d77ed6a943dc35662e949befa8725cfc7eead01a270417c8d07c1001b11623a088da8d2c9c34a41573314fa2394643147e4027e8a96a605 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/44107e81054aa5544b36eab8d811908a559b4d9027dc3fa1762c44e39551652199ef2a31f9bbcda79773c906151c12578cb18c43e0045de8b20c357272e1c62a +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/52ea57bde33e8ef4718d833c2df3cf0a9e90fdcd5715c0caad50b4e37ea60aca2b8c71096de6920dc936c40b6291e896293f91c7cfd2aa96cf6c2aa49ef662c8 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/5ea52f6d4771afda4747b9f44954102c02ae2d0686f8aa9eca36c29796bbba0d14e851c0dca0a6af17bfbbbac174e3be645ae708d958390168e85c64786fc9ef +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/86a1cfa113399acc7dd2dc90262a845558affb5e9373b6300dff68a485482c5e17ace9466bbc23b4301013ca1a27a577ca57ea838113f45bf321a64a242b1ad3 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/8ef99a12ae0624db198d9ecd83014fb8353c4731e0ba9a472f1fc339784308e38f1287d0765a7b0444f1a89c218c76a820dc4c9a3a39c1cedcd7423a4f5f88dc +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/cb3e8da2bf2b7efae4a6e8fd0b8562dacd16ca0531b173a91480a9e60ee795ac5bec13fc6eb461e03edc9a26f5ff1d5ba53521a1c1a6c1ee1765b544b7d7bf73 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/segments/d27ad326963b75736b636adad9fb812eb3f2871e0efb4bc7db37d4b701a4282911eaaee91bed3a759e940769b667be1ed66f2d7f2f41ac3906b87ab7eec19c3a +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/index/type +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_heads/heads/891d7ed26f62b0f8757b081e9d76840636877f1613ee17b7695fe2ee8640258c56e01291934e3797728bc2300a1b5f41e3f3ef81ab532c0e5ab475bb9b6f097a +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_heads/type +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/282ad8cf3324b2679a7d460c0fc324adfa21dcfad2f197ac6991b98ec91f98495bb3ddb2cba36ce5dfa28a52063a373bb03f5d2e34f6e0c7b6b81b3046a4d7d0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/891d7ed26f62b0f8757b081e9d76840636877f1613ee17b7695fe2ee8640258c56e01291934e3797728bc2300a1b5f41e3f3ef81ab532c0e5ab475bb9b6f097a +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/8926397375b8328137652c18cd4371808b214cb26864d77dd33c8eea895e10e62c297064c3d4c703d58ee11dee81bbf9851525e3a0c33151f54c164b8a4343b0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/89d29d938e64b1295e138faedfa5df6f6729a67e5ba8e0c3fdd1c9266e0f59a4bc35b8a21e14225072317879af3323fa0457e01d9b08608770bc23957feec6c0 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/95fc2854c0fa2528b03e2bcabc7612f56f0bcb3d6f54a06293e21d1887885a449907b5b320a1a94bac712bf56fdad0a4184b17047c1fc179dfa83acab3f70d21 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/9a58ceae46de4649375b6b880b8500c85d34c8e9bc650dc4c993afb2fa8d45c4180331821c6226a0fb1475b04a0b9a849502647ef9c4dcac74769eb501c885fa +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/a19ae062208aa20b7310705af3d26ef53095a9dabfad080883cc7a32e98687063179db95cb2c71ef9064801c59fa46d261f172d83e83f96a39c274387b59dca4 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/d1169a8d10067493e42752c2a7615ff27f55bd90c38b91feef918958e29a2239ee289a0cde8385441d1f0fe9af1fb634fa6d56438b9f33ec382e81fa59d70b54 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/e171e5c6039c050c2368584a537d36df221b1f9f23d285c0399b95b14608d67006229823e6831bff7d8b0c2f9e86ebaec9c6811461119195f430ade055073fed +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/operations/ee118dd19aac4e1cb354f83a37dadca70bc5f086cc8d36cd0059b222bf8c7250824401ff681518d0120f8db1f96f0025c464dcfc3b1ee28777e8c76325760134 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/type +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/123b0e36150cf8e99d644d2dfd1a7b0c8d2f676a78248d6902516d9ae58903665c79ec3f5f6729b98a1237dcf2abc1d41e690ae0ad30888c84786bcc9de5e314 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/2ac0b7d8b1fdfac82b3ff3926e0018f72ef2b48f85b41f5fa541271370e1197d41e37c2c6d62af0c6974658c4a9e5e945b8efcbcc7748bdd99bd9483f7e13e22 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/67b8396b301935ff624ff98952c57d8ee021e7d885e1220053b993e7bc822a4cf061298e9643ded745c55f3ed8e923a6731524129993d2664f48b60660761145 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/9c6fca696f77383cc87d068fe3f5912466b157dccc6465973e653dd4d2c02e2eca9c0725c071857bf3f4f0e263259eafc18f1739615a501e672bb2afd415316b +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/cae2e3e5952cb5ba93f27e3898d90dac6c41fc9e20c66ef0791e71b91dc103d924996c921c88179705264a224fb5d2bb29091fc8f18f0ea7a088aaabb859ea2a +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/cd8efb6da14127c81c37b56ead18a39b30f2cd154891185a6e906efb491dbf63f290eed3c31d4725f490a93208ffbe5cdc031d5b6de38fcb77cbb11f0357118f +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/e434359d6306a4f6997733b9b5308299984f05219c92e1b2a31f1203126be0fada5fc09a2ba86c98d1318981cb53997c7f8674ef25da6ca7bf9fd849598cd355 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/ea75e7ccb42f52b013dc1b45bb4e6d692b37c5e38bd39356ba2806be83ca5ca03ce20481db2788428126f93663e6723f99f4d46c5c705f5b12b70e2127ab15ba +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/fa5dec7ee06fbc6cbb2798c8e98bab482a6750776de41406fb06893c83f71f5015ec7ee2873642714b7bbc1c496f880e3acf44ffdabf2f87a39e0fbd68a4cc46 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/op_store/views/ffde172c6285c71851b22780e34962d8f9234067af59ff2dc38b74e905dc540fe35b944e8c0b2d4230dfcf778424a335494cd0aeea1bb4be2a11c4b5428ad465 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/14e3f15273e204cadfe38dd2f38cebd1343a6bfbb91c6af0f5f9de6e9003d8ec8b0eb676975af9dca2d06ee5e6b3886c3c5d3755c6f149b6b09172cccc35adff +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/42e0f32dd10ba6ae9f2297ca8ad0bd16337f14545b29f956ce380a7ab92bab771cbc9e04755752a6fa13231286f724379d6662b0fb257ef2cc2c52fe680eb95d +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/466a441e56afdde383cef1d6127ca1d4c825157feb65a59f8f6ff5fa7a523bd683d0b7a6bb16c00afff53a890319a472d98da9af3dea5675168a4d424aa7af32 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/482ae5a29fbe856c7272f2071b8b0f0359ee2d89ff392b8a900643fbd0836eccd067b8bf41909e206c90d45d6e7d8b6686b93ecaee5fe1a9060d87b672101310 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/5403fa06049419ddcf620ce0dc20911583e1b1062b42630ee325aa5ac2f918dd266dfed93ebc808a2aecb37ac3a33e251fe1396fd24b8ed566bfbd61a81ff959 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/56119fa0480cb66159978b1a8c9b031f9e978b7a3172eb87407a701fea34fdd90db7771cd433a2cf7e8a84a7b49c924f973eccb0eb05685ec42f36f7ef61cc06 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/90b1f4de4ba65e0652f1de45e4c84b623f99bd9d9453667e19c7040857bd397e59e7a84406f2ab6204d25789d995c47c350d8abc47a4bbb102059f3111f20028 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/9215bd4ac28fdecba111c63bace46d0f1c253ad3af44e0e74bd43d30757bef2e655538c1172c8f25769bce0a3c669b713440773c1859f0a136d9ca42501f2470 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/a79320f784ef97b3d6297e55a48b17a517a38d95d5c61ba8d01c59d68dcd2ccf3a96479f4fc3c4cafdcd56dc7bd58b1cb987e079764c1646533ab32418900727 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/abe6b7a350e1604cb8f5a2cd10cef13a019bd797770e6dc37414d33d98dcf36d8ec80a2ae13bd7dd2d2076772c0ffadec0b53ba72f4669b461fff2da5d30f1ba +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/b7689d54193a3798edd58d758966ad65ad57297c5276eab3e4ef07380779363efe9e462a149f4d42f55bbe004eb5ba88bf35df4c78ac975275530382390159d6 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/e75f5f3431d172d7e9434dfaef2be50812105b3ead73eeb10345c9b6892e9cbb5ee0602ebb0ceaf5ab87d22f45930dc30d136d0aac77310fb0261b3857ffde9b +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/extra/heads/56119fa0480cb66159978b1a8c9b031f9e978b7a3172eb87407a701fea34fdd90db7771cd433a2cf7e8a84a7b49c924f973eccb0eb05685ec42f36f7ef61cc06 +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/git_target +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/store/type +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/submodule_store/type +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/repo/workspace_store/index +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/working_copy/checkout +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/working_copy/tree_state +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/.jj/working_copy/type +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/LICENSE +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/docs/cloud.md +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/docs/gen_assets.sh +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/docs/index.md +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/mkdocs.yml +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/formats/__init__.py +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/handlers/cli_table.py +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/style.py +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/src/tab_cli/url_parser.py +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/tests/__init__.py +0 -0
- {tab_cli-0.1.6 → tab_cli-0.1.8}/tests/assets/test.csv +0 -0
|
@@ -43,7 +43,7 @@ Use it as the default operating guide when changing code in this repo.
|
|
|
43
43
|
- The CLI tests rely on `typer.testing.CliRunner`.
|
|
44
44
|
- Test data is stored under `tests/assets`.
|
|
45
45
|
- Existing tests emphasize user-visible CLI output, not internal implementation details.
|
|
46
|
-
-
|
|
46
|
+
- The CLI tests are split across focused files under `tests/`; extend the nearest existing file unless a new one is clearly warranted.
|
|
47
47
|
- Assert both `exit_code` and key output fragments.
|
|
48
48
|
- For stdin support, pass `"-"` as the path and provide `input=` to `runner.invoke(...)`.
|
|
49
49
|
|
|
@@ -89,6 +89,9 @@ Use it as the default operating guide when changing code in this repo.
|
|
|
89
89
|
|
|
90
90
|
## Types
|
|
91
91
|
|
|
92
|
+
- NEVER implicitly cast any variable to bool with `if var:` or `if not var:` unless the variable is already a bool. Do NOT rely on truthiness for control flow:
|
|
93
|
+
for example, testing if a list is empty with `if not my_list:` is not allowed. Instead, use explicit length checks like `if len(my_list) > 0:`.
|
|
94
|
+
always write `if x is not None:` or `if x is None:` when checking for `None` values.
|
|
92
95
|
- Type hints are used widely and should be preserved.
|
|
93
96
|
- Prefer modern built-in generics like `list[str]` and `dict[str, Any]`.
|
|
94
97
|
- Use `X | None` instead of `Optional[X]` in new code unless matching nearby style requires otherwise.
|
|
@@ -118,6 +121,7 @@ Use it as the default operating guide when changing code in this repo.
|
|
|
118
121
|
## Logging And Output
|
|
119
122
|
|
|
120
123
|
- The CLI configures Loguru with `RichHandler` in the Typer callback.
|
|
124
|
+
- When writing Loguru messages, use f-strings instead of Loguru brace-style formatting.
|
|
121
125
|
- User-facing table and summary output is rendered with Rich.
|
|
122
126
|
- Streaming command output usually writes bytes to `sys.stdout.buffer`.
|
|
123
127
|
- Keep stderr/stdout behavior consistent with the existing command design.
|
|
@@ -155,4 +159,4 @@ Use it as the default operating guide when changing code in this repo.
|
|
|
155
159
|
- Run `uv run pytest` for broader validation before finalizing cross-cutting changes.
|
|
156
160
|
- Mention pre-existing lint or type-check failures separately from regressions you introduce.
|
|
157
161
|
- Update CHANGELOG.md when necessary.
|
|
158
|
-
-
|
|
162
|
+
-
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
- 0.1.8:
|
|
2
|
+
- Improved `tab view` performance for partitioned directories by reading only as many early partitions as needed for an unfiltered preview.
|
|
3
|
+
- Added glob-pattern support for multi-file inputs such as `s3://.../date=*/*.parquet`.
|
|
4
|
+
- Speed up Parquet row counting in summaries by reading footer metadata instead of scanning file contents.
|
|
5
|
+
- Fixed S3 Polars `storage_options` to avoid nested `client_kwargs` values that could break native reads.
|
|
6
|
+
- Added `default_num_view_rows` config so the default `tab view` preview size can be customized.
|
|
7
|
+
- Added `log_level` config so the CLI log level can default from `~/.config/tab/config.json` when `--log-level` is omitted.
|
|
8
|
+
- Added `max_cell_length` config so `tab view` can default to truncating long cell values without passing `--max-cell-len` every time.
|
|
9
|
+
- Added `num_remote_workers` config to parallelize remote per-partition summary row counting.
|
|
10
|
+
- Validated config file value types instead of silently accepting invalid JSON types.
|
|
11
|
+
- Fixed the developer `Makefile` targets to point at `src/tab_cli`.
|
|
12
|
+
- Tightened multi-file summary validation to reject inconsistent schemas, not just mismatched column counts.
|
|
13
|
+
- `tab cat` now rejects mixed input formats with a clear error instead of reusing the first reader implicitly.
|
|
14
|
+
- Cleaned up package metadata and repository hygiene issues including version drift and `uv.lock` ignore rules.
|
|
15
|
+
- 0.1.7:
|
|
16
|
+
- Optional dependency groups are now named `tab-cli[s3|gs|az]`, in accordance with the protocol name.
|
|
17
|
+
- 0.1.6:
|
|
18
|
+
- Fixed bug in pyarrow loading of Parquet files.
|
|
19
|
+
- Added global config file support: settings can be persisted in `~/.config/tab/config.json`. Config file values serve as defaults that CLI flags override.
|
|
20
|
+
- 0.1.5:
|
|
21
|
+
- Added stdin support: use `-` as the file path to read from stdin (e.g. `cat data.csv | tab view -i csv -`). Requires `-i`/`--input-format` since format cannot be inferred. Works with `view`, `schema`, `summary`, `convert`, and `cat`.
|
|
22
|
+
- Added row-wise JMESPath queries via `--jmespath` / `--jp` on `view`, `convert`, and `cat`. Object results become columns; non-object results go into a `value` column. `--sql` and `--jp` are mutually exclusive.
|
|
23
|
+
- Implemented `--jp` with `LazyFrame.map_batches(...)` so row reshaping stays batch-oriented instead of materializing the full transformed dataset up front.
|
|
24
|
+
- 0.1.4:
|
|
25
|
+
- Removed `tab sql` subcommand; SQL is now a `--sql` option on `tab view`, `tab convert`, and `tab cat`.
|
|
26
|
+
- Automatic PyArrow fallback for Parquet files that fail to read with Polars' native reader.
|
|
27
|
+
- 0.1.3:
|
|
28
|
+
- Separate `tab view` from `tab cat`: `tab view` does not convert formats, `tab cat` does.
|
|
29
|
+
- Added `--max-cell-len` option to `tab view` to truncate long cell contents.
|
|
30
|
+
- 0.1.2:
|
|
31
|
+
- Bugfix on reading directories.
|
|
32
|
+
- 0.1.1:
|
|
33
|
+
- Better credential handling for Azure Blob Storage and Google Cloud Storage.
|
|
34
|
+
- 0.1.0: Initial release
|
|
@@ -13,13 +13,13 @@ clean:
|
|
|
13
13
|
find . -type d -name __pycache__ -exec rm -rf {} +
|
|
14
14
|
|
|
15
15
|
lint:
|
|
16
|
-
uv run ruff check tab_cli
|
|
16
|
+
uv run ruff check src/tab_cli tests
|
|
17
17
|
|
|
18
18
|
format:
|
|
19
|
-
uv run ruff format tab_cli
|
|
19
|
+
uv run ruff format src/tab_cli tests
|
|
20
20
|
|
|
21
21
|
typecheck:
|
|
22
|
-
uv run ty check tab_cli
|
|
22
|
+
uv run ty check src/tab_cli
|
|
23
23
|
|
|
24
24
|
test:
|
|
25
25
|
uv run pytest
|
tab_cli-0.1.8/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tab-cli
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: A CLI tool for tabular data
|
|
5
|
+
Author-email: Tongfei Chen <tongfei@pm.me>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: blobfile>=3.0
|
|
9
|
+
Requires-Dist: fsspec>=2025.1.0
|
|
10
|
+
Requires-Dist: jmespath>=1.0
|
|
11
|
+
Requires-Dist: loguru>=0.7.3
|
|
12
|
+
Requires-Dist: polars-fastavro>=0.5.1
|
|
13
|
+
Requires-Dist: polars>=1.0
|
|
14
|
+
Requires-Dist: pyarrow>=15.0
|
|
15
|
+
Requires-Dist: rich>=13.0
|
|
16
|
+
Requires-Dist: typer>=0.21.1
|
|
17
|
+
Provides-Extra: az
|
|
18
|
+
Requires-Dist: adlfs>=2025.1.0; extra == 'az'
|
|
19
|
+
Requires-Dist: azure-identity>=1.10.0; extra == 'az'
|
|
20
|
+
Provides-Extra: gs
|
|
21
|
+
Requires-Dist: gcsfs>=2025.1.0; extra == 'gs'
|
|
22
|
+
Requires-Dist: google-auth>=2.27.0; extra == 'gs'
|
|
23
|
+
Provides-Extra: s3
|
|
24
|
+
Requires-Dist: boto3>=1.28.0; extra == 's3'
|
|
25
|
+
Requires-Dist: s3fs>=2025.1.0; extra == 's3'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# tab
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/tab-cli/)
|
|
31
|
+
|
|
32
|
+
A CLI tool for viewing, querying, and converting tabular data files.
|
|
33
|
+
Reads CSV, TSV, JSON Lines, Parquet, and Avro -- locally or from S3, GCS, and Azure Blob Storage.
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
pip install tab-cli
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Documentation**: [tongfei.me/tab](https://tongfei.me/tab)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Quick look
|
|
44
|
+
|
|
45
|
+
### View any tabular file
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
tab view data.csv
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="https://raw.githubusercontent.com/ctongfei/tab/refs/heads/gh-pages/assets/test.svg" alt="tab view" width="680">
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
### Query with SQL
|
|
56
|
+
|
|
57
|
+
The table is always available as `t`:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
tab view --sql 'SELECT * FROM t WHERE Metric_A_Value > 80' data.csv
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
<p align="center">
|
|
64
|
+
<img src="https://raw.githubusercontent.com/ctongfei/tab/refs/heads/gh-pages/assets/test-where.svg" alt="tab view --sql" width="680">
|
|
65
|
+
</p>
|
|
66
|
+
|
|
67
|
+
### Reshape rows with JMESPath
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
tab view --jp '{id: participant.id, city: profile.address.city}' data.parquet
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Convert between formats
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
tab convert data.csv data.parquet
|
|
77
|
+
tab convert data.parquet data.jsonl -o jsonl
|
|
78
|
+
tab convert data.csv output_dir/ -o parquet -n 4 # partitioned
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Concatenate files
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
tab cat part1.csv part2.csv part3.csv -o jsonl > combined.jsonl
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Inspect schema and summary
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
tab schema data.parquet
|
|
91
|
+
tab summary data.parquet
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Read from stdin
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
curl -s https://example.com/data.csv | tab view -i csv -
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Read from cloud storage
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
tab view s3://bucket/path/data.parquet
|
|
104
|
+
tab view gs://bucket/path/data.csv
|
|
105
|
+
tab view az://container/path/data.jsonl
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Install cloud extras as needed:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
pip install 'tab-cli[s3]' # AWS S3
|
|
112
|
+
pip install 'tab-cli[gs]' # Google Cloud Storage
|
|
113
|
+
pip install 'tab-cli[az]' # Azure Blob Storage
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Supported formats
|
|
117
|
+
- csv
|
|
118
|
+
- tsv
|
|
119
|
+
- jsonl
|
|
120
|
+
- parquet
|
|
121
|
+
- avro
|
tab_cli-0.1.8/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# tab
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/tab-cli/)
|
|
4
|
+
|
|
5
|
+
A CLI tool for viewing, querying, and converting tabular data files.
|
|
6
|
+
Reads CSV, TSV, JSON Lines, Parquet, and Avro -- locally or from S3, GCS, and Azure Blob Storage.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
pip install tab-cli
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Documentation**: [tongfei.me/tab](https://tongfei.me/tab)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Quick look
|
|
17
|
+
|
|
18
|
+
### View any tabular file
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
tab view data.csv
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<img src="https://raw.githubusercontent.com/ctongfei/tab/refs/heads/gh-pages/assets/test.svg" alt="tab view" width="680">
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
### Query with SQL
|
|
29
|
+
|
|
30
|
+
The table is always available as `t`:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
tab view --sql 'SELECT * FROM t WHERE Metric_A_Value > 80' data.csv
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="https://raw.githubusercontent.com/ctongfei/tab/refs/heads/gh-pages/assets/test-where.svg" alt="tab view --sql" width="680">
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
### Reshape rows with JMESPath
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
tab view --jp '{id: participant.id, city: profile.address.city}' data.parquet
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Convert between formats
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
tab convert data.csv data.parquet
|
|
50
|
+
tab convert data.parquet data.jsonl -o jsonl
|
|
51
|
+
tab convert data.csv output_dir/ -o parquet -n 4 # partitioned
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Concatenate files
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
tab cat part1.csv part2.csv part3.csv -o jsonl > combined.jsonl
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Inspect schema and summary
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
tab schema data.parquet
|
|
64
|
+
tab summary data.parquet
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Read from stdin
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
curl -s https://example.com/data.csv | tab view -i csv -
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Read from cloud storage
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
tab view s3://bucket/path/data.parquet
|
|
77
|
+
tab view gs://bucket/path/data.csv
|
|
78
|
+
tab view az://container/path/data.jsonl
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Install cloud extras as needed:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
pip install 'tab-cli[s3]' # AWS S3
|
|
85
|
+
pip install 'tab-cli[gs]' # Google Cloud Storage
|
|
86
|
+
pip install 'tab-cli[az]' # Azure Blob Storage
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Supported formats
|
|
90
|
+
- csv
|
|
91
|
+
- tsv
|
|
92
|
+
- jsonl
|
|
93
|
+
- parquet
|
|
94
|
+
- avro
|
|
@@ -17,7 +17,7 @@ Options:
|
|
|
17
17
|
| `--jmespath` / `--jp` | JMESPath expression to apply to each row as JSON. Object outputs become columns; non-object outputs go to a `value` column. The result shape must stay consistent across rows. |
|
|
18
18
|
| `--limit` | Maximum number of rows to display. |
|
|
19
19
|
| `--skip` | Number of rows to skip from the beginning. |
|
|
20
|
-
| `--max-cell-len` | Truncate cell contents longer than this.
|
|
20
|
+
| `--max-cell-len` | Truncate cell contents longer than this. If omitted, `max_cell_length` from config is used when set. |
|
|
21
21
|
|
|
22
22
|
## `tab schema`
|
|
23
23
|
|
|
@@ -91,4 +91,4 @@ Options:
|
|
|
91
91
|
| Option | Description |
|
|
92
92
|
|-------------------------|------------------------------------------------------------------------------------------------------------------------------|
|
|
93
93
|
| `--az-url-authority-is-account` | Interpret az:// URL authority as storage account name instead of container name. See [azure.md](Azure) for more information. |
|
|
94
|
-
| `--log-level` | Log level from `{DEBUG, INFO, WARNING, ERROR, CRITICAL}`.
|
|
94
|
+
| `--log-level` | Log level from `{DEBUG, INFO, WARNING, ERROR, CRITICAL}`. If omitted, uses `log_level` from config. |
|
|
@@ -11,6 +11,10 @@ mkdir -p ~/.config/tab
|
|
|
11
11
|
cat > ~/.config/tab/config.json << 'EOF'
|
|
12
12
|
{
|
|
13
13
|
"az_url_authority_is_account": false,
|
|
14
|
+
"default_num_view_rows": 20,
|
|
15
|
+
"log_level": "INFO",
|
|
16
|
+
"max_cell_length": null,
|
|
17
|
+
"num_remote_workers": 8,
|
|
14
18
|
"sampling_size_for_schema_inference": 32
|
|
15
19
|
}
|
|
16
20
|
EOF
|
|
@@ -21,6 +25,10 @@ EOF
|
|
|
21
25
|
| Key | Type | Default | Description |
|
|
22
26
|
|-----|------|---------|-------------|
|
|
23
27
|
| `az_url_authority_is_account` | `bool` | `false` | Interpret `az://` URL authority as storage account name instead of container name. |
|
|
28
|
+
| `default_num_view_rows` | `int` | `20` | Default number of rows shown by `tab view` when `--limit` is omitted. |
|
|
29
|
+
| `log_level` | `str` | `"INFO"` | Default CLI log level when `--log-level` is omitted. |
|
|
30
|
+
| `max_cell_length` | `int \| null` | `null` | Default maximum cell length for `tab view`. The CLI `--max-cell-len` flag overrides it. |
|
|
31
|
+
| `num_remote_workers` | `int` | `8` | Maximum worker threads for remote per-partition summary work such as Parquet row counts. |
|
|
24
32
|
| `sampling_size_for_schema_inference` | `int` | `32` | Number of rows sampled for schema inference (e.g. when using `--jp`). |
|
|
25
33
|
|
|
26
34
|
## Precedence
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tab-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.8"
|
|
4
4
|
description = "A CLI tool for tabular data"
|
|
5
5
|
authors = [{name = "Tongfei Chen", email = "tongfei@pm.me"}]
|
|
6
6
|
readme = "README.md"
|
|
@@ -20,8 +20,8 @@ dependencies = [
|
|
|
20
20
|
|
|
21
21
|
[project.optional-dependencies]
|
|
22
22
|
s3 = ["s3fs>=2025.1.0", "boto3>=1.28.0"]
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
gs = ["gcsfs>=2025.1.0", "google-auth>=2.27.0"]
|
|
24
|
+
az = ["adlfs>=2025.1.0", "azure-identity>=1.10.0"]
|
|
25
25
|
|
|
26
26
|
[dependency-groups]
|
|
27
27
|
dev = [
|
|
@@ -79,6 +79,8 @@ app = typer.Typer(
|
|
|
79
79
|
no_args_is_help=True,
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
+
DEFAULT_VIEW_TRUNCATION_PROBE_ROWS = 1
|
|
83
|
+
|
|
82
84
|
|
|
83
85
|
@app.callback()
|
|
84
86
|
def main_callback(
|
|
@@ -90,13 +92,18 @@ def main_callback(
|
|
|
90
92
|
),
|
|
91
93
|
] = False,
|
|
92
94
|
log_level: Annotated[
|
|
93
|
-
str,
|
|
95
|
+
str | None,
|
|
94
96
|
typer.Option(
|
|
95
|
-
"--log-level",
|
|
97
|
+
"--log-level",
|
|
98
|
+
help="Log level from {DEBUG, INFO, WARNING, ERROR, CRITICAL}; defaults to config when omitted",
|
|
96
99
|
),
|
|
97
|
-
] =
|
|
100
|
+
] = None,
|
|
98
101
|
) -> None:
|
|
99
102
|
"""Global options for tab_cli CLI."""
|
|
103
|
+
load_config_file()
|
|
104
|
+
effective_log_level = (
|
|
105
|
+
log_level.upper() if log_level is not None else config.config.log_level.upper()
|
|
106
|
+
)
|
|
100
107
|
logger.remove()
|
|
101
108
|
logger.add(
|
|
102
109
|
RichHandler(
|
|
@@ -105,9 +112,8 @@ def main_callback(
|
|
|
105
112
|
markup=True,
|
|
106
113
|
),
|
|
107
114
|
format="{message}",
|
|
108
|
-
level=
|
|
115
|
+
level=effective_log_level,
|
|
109
116
|
)
|
|
110
|
-
load_config_file()
|
|
111
117
|
# CLI flags override config file values
|
|
112
118
|
if az_url_authority_is_account:
|
|
113
119
|
config.config.az_url_authority_is_account = az_url_authority_is_account
|
|
@@ -176,12 +182,20 @@ def _apply_jmespath(lf: pl.LazyFrame, expression: str) -> pl.LazyFrame:
|
|
|
176
182
|
|
|
177
183
|
compiled = jmespath.compile(expression)
|
|
178
184
|
sample_df = lf.slice(0, Config.sampling_size_for_schema_inference).collect()
|
|
185
|
+
logger.debug(
|
|
186
|
+
"Inferring JMESPath output schema from "
|
|
187
|
+
f"{Config.sampling_size_for_schema_inference} sampled row(s)"
|
|
188
|
+
)
|
|
179
189
|
if sample_df.is_empty():
|
|
190
|
+
logger.debug("JMESPath schema inference sample was empty; returning empty LazyFrame")
|
|
180
191
|
return pl.DataFrame().lazy()
|
|
181
192
|
|
|
182
193
|
transformed_sample, result_mode = _transform_jmespath_batch(sample_df, compiled)
|
|
183
194
|
output_schema = transformed_sample.schema
|
|
184
195
|
expected_columns = tuple(transformed_sample.columns)
|
|
196
|
+
logger.debug(
|
|
197
|
+
f"Inferred JMESPath result mode '{result_mode}' with columns {expected_columns}"
|
|
198
|
+
)
|
|
185
199
|
|
|
186
200
|
return lf.map_batches(
|
|
187
201
|
lambda batch: _transform_jmespath_batch(
|
|
@@ -223,16 +237,119 @@ def _apply_limit(
|
|
|
223
237
|
and returns whether the data was truncated.
|
|
224
238
|
"""
|
|
225
239
|
if limit is None and default_limit is not None:
|
|
240
|
+
logger.debug(
|
|
241
|
+
f"Applying inferred default row limit {default_limit} with skip {skip}"
|
|
242
|
+
)
|
|
226
243
|
lf = lf.slice(skip, length=default_limit + 1)
|
|
227
244
|
df = lf.collect()
|
|
228
245
|
truncated = len(df) > default_limit
|
|
229
246
|
if truncated:
|
|
230
247
|
df = df.head(default_limit)
|
|
248
|
+
logger.debug("Detected truncated preview after applying inferred default row limit")
|
|
231
249
|
return df.lazy(), truncated
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
250
|
+
if skip > 0 or limit is not None:
|
|
251
|
+
lf = lf.slice(skip, length=limit)
|
|
252
|
+
return lf, False
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _read_source(path: str, input_format: str | None) -> tuple[pl.LazyFrame, str | None]:
|
|
256
|
+
"""Read a source path and return its LazyFrame and inferred format."""
|
|
257
|
+
if is_stdin(path):
|
|
258
|
+
logger.debug(
|
|
259
|
+
"Using stdin source with explicit format "
|
|
260
|
+
f"'{input_format.lower() if input_format is not None else None}'"
|
|
261
|
+
)
|
|
262
|
+
return (
|
|
263
|
+
read_stdin(format=input_format),
|
|
264
|
+
input_format.lower() if input_format is not None else None,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
reader = infer_reader(path, format=input_format)
|
|
268
|
+
logger.debug(
|
|
269
|
+
f"Read source '{path}' using inferred format '{reader.format.extension()}'"
|
|
270
|
+
)
|
|
271
|
+
return reader.read(path), reader.format.extension()
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _prepare_view_frame(
|
|
275
|
+
path: str,
|
|
276
|
+
input_format: str | None,
|
|
277
|
+
sql: str | None,
|
|
278
|
+
jmespath_expr: str | None,
|
|
279
|
+
limit: int | None,
|
|
280
|
+
skip: int,
|
|
281
|
+
) -> tuple[pl.LazyFrame, bool]:
|
|
282
|
+
"""Prepare the LazyFrame used by `tab view` and report truncation."""
|
|
283
|
+
default_view_rows = config.config.default_num_view_rows
|
|
284
|
+
|
|
285
|
+
if is_stdin(path):
|
|
286
|
+
logger.debug("Preparing view for stdin input")
|
|
287
|
+
lf = read_stdin(format=input_format)
|
|
288
|
+
lf = _apply_query(lf, sql=sql, jmespath_expr=jmespath_expr)
|
|
289
|
+
return _apply_limit(
|
|
290
|
+
lf,
|
|
291
|
+
limit=limit,
|
|
292
|
+
skip=skip,
|
|
293
|
+
default_limit=default_view_rows if limit is None else None,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
reader = infer_reader(path, format=input_format)
|
|
297
|
+
if sql is None and jmespath_expr is None:
|
|
298
|
+
preview_limit = (
|
|
299
|
+
limit
|
|
300
|
+
if limit is not None
|
|
301
|
+
else default_view_rows + DEFAULT_VIEW_TRUNCATION_PROBE_ROWS
|
|
302
|
+
)
|
|
303
|
+
logger.debug(
|
|
304
|
+
f"Using preview read for '{path}' with inferred preview limit "
|
|
305
|
+
f"{preview_limit} and skip {skip}"
|
|
306
|
+
)
|
|
307
|
+
lf = reader.read_preview(path, limit=preview_limit, offset=skip)
|
|
308
|
+
if limit is not None:
|
|
309
|
+
return lf, False
|
|
310
|
+
|
|
311
|
+
df = lf.collect()
|
|
312
|
+
truncated = len(df) > default_view_rows
|
|
313
|
+
if truncated:
|
|
314
|
+
df = df.head(default_view_rows)
|
|
315
|
+
return df.lazy(), truncated
|
|
316
|
+
|
|
317
|
+
logger.debug(f"Using full read for '{path}' because a query transform was provided")
|
|
318
|
+
lf = reader.read(path)
|
|
319
|
+
lf = _apply_query(lf, sql=sql, jmespath_expr=jmespath_expr)
|
|
320
|
+
return _apply_limit(
|
|
321
|
+
lf,
|
|
322
|
+
limit=limit,
|
|
323
|
+
skip=skip,
|
|
324
|
+
default_limit=default_view_rows if limit is None else None,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _resolve_cat_output_format(
|
|
329
|
+
paths: list[str],
|
|
330
|
+
input_format: str | None,
|
|
331
|
+
) -> tuple[list[pl.LazyFrame], str | None]:
|
|
332
|
+
"""Read all inputs for `tab cat` and validate format consistency."""
|
|
333
|
+
files: list[pl.LazyFrame] = []
|
|
334
|
+
resolved_format = input_format.lower() if input_format is not None else None
|
|
335
|
+
if resolved_format is not None:
|
|
336
|
+
logger.debug(f"Using explicit shared input format '{resolved_format}' for `tab cat`")
|
|
337
|
+
|
|
338
|
+
for path in paths:
|
|
339
|
+
lf, current_format = _read_source(path, input_format)
|
|
340
|
+
if current_format is not None:
|
|
341
|
+
if resolved_format is None:
|
|
342
|
+
resolved_format = current_format
|
|
343
|
+
logger.debug(
|
|
344
|
+
f"Inferred shared `tab cat` format '{resolved_format}' from '{path}'"
|
|
345
|
+
)
|
|
346
|
+
elif current_format != resolved_format:
|
|
347
|
+
raise ValueError(
|
|
348
|
+
"All inputs to `tab cat` must use the same format unless -i/--input-format is provided"
|
|
349
|
+
)
|
|
350
|
+
files.append(lf)
|
|
351
|
+
|
|
352
|
+
return files, resolved_format
|
|
236
353
|
|
|
237
354
|
|
|
238
355
|
@app.command()
|
|
@@ -247,19 +364,25 @@ def view(
|
|
|
247
364
|
table_svg: TableSvgOpt = False,
|
|
248
365
|
) -> None:
|
|
249
366
|
"""View tabular data as a formatted table."""
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
367
|
+
effective_max_cell_len = (
|
|
368
|
+
max_cell_len if max_cell_len is not None else config.config.max_cell_length
|
|
369
|
+
)
|
|
370
|
+
if max_cell_len is None and effective_max_cell_len is not None:
|
|
371
|
+
logger.debug(
|
|
372
|
+
f"Inferred max_cell_len={effective_max_cell_len} for `tab view` from config"
|
|
373
|
+
)
|
|
374
|
+
lf, truncated = _prepare_view_frame(
|
|
375
|
+
path,
|
|
376
|
+
input_format=input,
|
|
377
|
+
sql=sql,
|
|
378
|
+
jmespath_expr=jmespath_expr,
|
|
379
|
+
limit=limit,
|
|
380
|
+
skip=skip,
|
|
258
381
|
)
|
|
259
382
|
writer = infer_writer(
|
|
260
383
|
"table-svg" if table_svg else None,
|
|
261
384
|
truncated=truncated,
|
|
262
|
-
max_cell_len=
|
|
385
|
+
max_cell_len=effective_max_cell_len,
|
|
263
386
|
)
|
|
264
387
|
for chunk in writer.write(lf):
|
|
265
388
|
sys.stdout.buffer.write(chunk)
|
|
@@ -278,7 +401,7 @@ def schema(
|
|
|
278
401
|
else:
|
|
279
402
|
reader = infer_reader(path, format=input)
|
|
280
403
|
table_schema = reader.schema(path)
|
|
281
|
-
console = Console(
|
|
404
|
+
console = Console()
|
|
282
405
|
console.print(table_schema)
|
|
283
406
|
|
|
284
407
|
|
|
@@ -299,7 +422,7 @@ def summary(
|
|
|
299
422
|
else:
|
|
300
423
|
handler = infer_reader(path, format=input)
|
|
301
424
|
table_summary = handler.summary(path)
|
|
302
|
-
console = Console(
|
|
425
|
+
console = Console()
|
|
303
426
|
console.print(table_summary)
|
|
304
427
|
|
|
305
428
|
|
|
@@ -320,6 +443,9 @@ def convert(
|
|
|
320
443
|
if output is not None:
|
|
321
444
|
writer = infer_writer(format=output)
|
|
322
445
|
elif input is not None:
|
|
446
|
+
logger.debug(
|
|
447
|
+
f"Inferred convert output format '{input.lower()}' from stdin input format"
|
|
448
|
+
)
|
|
323
449
|
writer = infer_writer(format=input)
|
|
324
450
|
else:
|
|
325
451
|
raise ValueError(
|
|
@@ -333,9 +459,15 @@ def convert(
|
|
|
333
459
|
if output is not None:
|
|
334
460
|
writer = infer_writer(format=output)
|
|
335
461
|
elif input is not None:
|
|
462
|
+
logger.debug(
|
|
463
|
+
f"Inferred convert output format '{input.lower()}' from explicit input format override"
|
|
464
|
+
)
|
|
336
465
|
writer = infer_writer(format=input)
|
|
337
466
|
else:
|
|
338
467
|
writer = reader
|
|
468
|
+
logger.debug(
|
|
469
|
+
f"Inferred convert output format '{reader.format.extension()}' from source '{src}'"
|
|
470
|
+
)
|
|
339
471
|
assert isinstance(writer, TableWriter)
|
|
340
472
|
lf = reader.read(src)
|
|
341
473
|
lf = _apply_query(lf, sql=sql, jmespath_expr=jmespath_expr)
|
|
@@ -351,24 +483,14 @@ def cat(
|
|
|
351
483
|
jmespath_expr: JmespathOpt = None,
|
|
352
484
|
) -> None:
|
|
353
485
|
"""Concatenate tabular data from multiple files, or just print a single file."""
|
|
354
|
-
files
|
|
355
|
-
reader = None
|
|
356
|
-
for path in paths:
|
|
357
|
-
if is_stdin(path):
|
|
358
|
-
files.append(read_stdin(format=input))
|
|
359
|
-
else:
|
|
360
|
-
if reader is None:
|
|
361
|
-
reader = infer_reader(path, format=input)
|
|
362
|
-
files.append(reader.read(path))
|
|
486
|
+
files, resolved_format = _resolve_cat_output_format(paths, input)
|
|
363
487
|
lf = pl.concat(files, how="vertical")
|
|
364
488
|
lf = _apply_query(lf, sql=sql, jmespath_expr=jmespath_expr)
|
|
365
489
|
if output is not None:
|
|
366
490
|
writer = infer_writer(format=output)
|
|
367
|
-
elif
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
elif input is not None:
|
|
371
|
-
writer = infer_writer(format=input)
|
|
491
|
+
elif resolved_format is not None:
|
|
492
|
+
logger.debug(f"Inferred `tab cat` output format '{resolved_format}' from input sources")
|
|
493
|
+
writer = infer_writer(format=resolved_format)
|
|
372
494
|
assert isinstance(writer, TableWriter)
|
|
373
495
|
else:
|
|
374
496
|
raise ValueError(
|