Skip to content

Commit 586e38c

Browse files
committed
fix: Add glob, user dir and env var support on Windows #634
1 parent 77faf1f commit 586e38c

File tree

5 files changed

+72
-0
lines changed

5 files changed

+72
-0
lines changed

CHANGELOG.rst

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Unreleased
66
- feat: Add a Docker image.
77
- feat: Add man pages to the sdist and wheel distributions.
88
- fix: :doc:`/scripts/csvstat` no longer errors when a column is a time delta and :code:`--json` is set.
9+
- fix: When taking arguments from ``sys.argv`` on Windows, glob patterns, user directories, and environment variables are expanded.
910

1011
2.0.0 - May 1, 2024
1112
-------------------

csvkit/cli.py

+29
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
import gzip
88
import itertools
99
import lzma
10+
import os
11+
import re
1012
import sys
1113
import warnings
14+
from glob import glob
1215
from os.path import splitext
1316

1417
import agate
@@ -73,6 +76,11 @@ def __init__(self, args=None, output_file=None, error_file=None):
7376
"""
7477
Perform argument processing and other setup for a CSVKitUtility.
7578
"""
79+
if args is None:
80+
args = sys.argv[1:]
81+
if os.name == 'nt':
82+
args = _expand_args(args)
83+
7684
self._init_common_parser()
7785
self.add_arguments()
7886
self.args = self.argparser.parse_args(args)
@@ -550,3 +558,24 @@ def parse_column_identifiers(ids, column_names, column_offset=1, excluded_column
550558
excludes.append(match_column_identifier(column_names, x, column_offset))
551559

552560
return [c for c in columns if c not in excludes]
561+
562+
563+
# Adapted from https://github.com/pallets/click/blame/main/src/click/utils.py
564+
def _expand_args(args):
565+
out = []
566+
567+
for arg in args:
568+
arg = os.path.expanduser(arg)
569+
arg = os.path.expandvars(arg)
570+
571+
try:
572+
matches = glob(arg, recursive=True)
573+
except re.error:
574+
matches = []
575+
576+
if matches:
577+
out.extend(matches)
578+
else:
579+
out.append(arg)
580+
581+
return out

tests/test_utilities/test_csvjoin.py

+10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import os
12
import sys
3+
import unittest
24
from unittest.mock import patch
35

46
from csvkit.utilities.csvjoin import CSVJoin, launch_new_instance
@@ -34,6 +36,14 @@ def test_join_options(self):
3436
'You must provide join column names when performing an outer join.',
3537
)
3638

39+
@unittest.skipIf(os.name != 'nt', 'Windows only')
40+
def test_glob(self):
41+
self.assertRows(['--no-inference', '-c', 'a', 'examples/dummy?.csv'], [
42+
['a', 'b', 'c', 'b2', 'c2'],
43+
['1', '2', '3', '2', '3'],
44+
['1', '2', '3', '4', '5'],
45+
])
46+
3747
def test_sequential(self):
3848
output = self.get_output_as_io(['examples/join_a.csv', 'examples/join_b.csv'])
3949
self.assertEqual(len(output.readlines()), 4)

tests/test_utilities/test_csvsql.py

+18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import io
22
import os
33
import sys
4+
import unittest
45
from textwrap import dedent
56
from unittest.mock import patch
67

@@ -63,6 +64,23 @@ def tearDown(self):
6364
if os.path.exists(self.db_file):
6465
os.remove(self.db_file)
6566

67+
@unittest.skipIf(os.name != 'nt', 'Windows only')
68+
def test_glob(self):
69+
sql = self.get_output(['examples/dummy?.csv'])
70+
71+
self.assertEqual(sql.replace('\t', ' '), dedent('''\
72+
CREATE TABLE dummy2 (
73+
a BOOLEAN NOT NULL,
74+
b DECIMAL NOT NULL,
75+
c DECIMAL NOT NULL
76+
);
77+
CREATE TABLE dummy3 (
78+
a BOOLEAN NOT NULL,
79+
b DECIMAL NOT NULL,
80+
c DECIMAL NOT NULL
81+
);
82+
''')) # noqa: W291
83+
6684
def test_create_table(self):
6785
sql = self.get_output(['--tables', 'foo', 'examples/testfixed_converted.csv'])
6886

tests/test_utilities/test_csvstack.py

+14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import os
12
import sys
3+
import unittest
24
from unittest.mock import patch
35

46
from csvkit.utilities.csvstack import CSVStack, launch_new_instance
@@ -20,6 +22,18 @@ def test_options(self):
2022
'The number of grouping values must be equal to the number of CSV files being stacked.',
2123
)
2224

25+
@unittest.skipIf(os.name != 'nt', 'Windows only')
26+
def test_glob(self):
27+
self.assertRows(['examples/dummy*.csv'], [
28+
['a', 'b', 'c', 'd'],
29+
['1', '2', '3', ''],
30+
['1', '2', '3', ''],
31+
['1', '2', '3', ''],
32+
['1', '4', '5', ''],
33+
['1', '2', '3', ''],
34+
['1', '2', '3', '4'],
35+
])
36+
2337
def test_skip_lines(self):
2438
self.assertRows(['--skip-lines', '3', 'examples/test_skip_lines.csv', 'examples/test_skip_lines.csv'], [
2539
['a', 'b', 'c'],

0 commit comments

Comments
 (0)