9
9
contains a set of useful utilities for including npm packages
10
10
within a Python package.
11
11
"""
12
+ from collections import defaultdict
12
13
from os .path import join as pjoin
13
14
import io
14
15
import os
@@ -146,8 +147,9 @@ def create_cmdclass(prerelease_cmd=None, package_data_spec=None,
146
147
A dictionary whose keys are the dotted package names and
147
148
whose values are a list of glob patterns.
148
149
data_files_spec: list, optional
149
- A list of (path, patterns) tuples where the path is the
150
- `data_files` install path and the patterns are glob patterns.
150
+ A list of (path, dname, pattern) tuples where the path is the
151
+ `data_files` install path, dname is the source directory, and the
152
+ pattern is a glob pattern.
151
153
152
154
Notes
153
155
-----
@@ -159,9 +161,12 @@ def create_cmdclass(prerelease_cmd=None, package_data_spec=None,
159
161
name.
160
162
e.g. `dict(foo=['./bar/*', './baz/**'])`
161
163
162
- The data files glob patterns should be absolute paths or relative paths
163
- from the root directory of the repository.
164
- e.g. `('share/foo/bar', ['pkgname/bizz/*', 'pkgname/baz/**'])`
164
+ The data files directories should be absolute paths or relative paths
165
+ from the root directory of the repository. Data files are specified
166
+ differently from `package_data` because we need a separate path entry
167
+ for each nested folder in `data_files`, and this makes it easier to
168
+ parse.
169
+ e.g. `('share/foo/bar', 'pkgname/bizz, '*')`
165
170
"""
166
171
wrapped = [prerelease_cmd ] if prerelease_cmd else []
167
172
if package_data_spec or data_files_spec :
@@ -467,10 +472,10 @@ def run(self):
467
472
raise
468
473
else :
469
474
pass
470
-
471
- result = cls .run (self )
472
475
# update package data
473
476
update_package_data (self .distribution )
477
+
478
+ result = cls .run (self )
474
479
return result
475
480
return WrappedCommand
476
481
@@ -483,20 +488,58 @@ class FileHandler(BaseCommand):
483
488
def run (self ):
484
489
package_data = self .distribution .package_data
485
490
package_spec = package_data_spec or dict ()
486
- data_spec = data_files_spec or []
487
491
488
492
for (key , patterns ) in package_spec .items ():
489
493
package_data [key ] = _get_package_data (key , patterns )
490
494
491
- data_files = self .distribution .data_files or []
492
- for (path , patterns ) in data_spec :
493
- data_files .append ((path , _get_files (patterns )))
494
-
495
- self .distribution .data_files = data_files
495
+ self .distribution .data_files = _get_data_files (
496
+ data_files_spec , self .distribution .data_files
497
+ )
496
498
497
499
return FileHandler
498
500
499
501
502
+ def _get_data_files (data_specs , existing ):
503
+ """Expand data file specs into valid data files metadata.
504
+
505
+ Parameters
506
+ ----------
507
+ data_specs: list of tuples
508
+ See [createcmdclass] for description.
509
+ existing: list of tuples
510
+ The existing distrubution data_files metadata.
511
+
512
+ Returns
513
+ -------
514
+ A valid list of data_files items.
515
+ """
516
+ # Extract the existing data files into a staging object.
517
+ file_data = defaultdict (list )
518
+ for (path , files ) in existing or []:
519
+ file_data [path ] = files
520
+
521
+ # Extract the files and assign them to the proper data
522
+ # files path.
523
+ for (path , dname , pattern ) in data_specs or []:
524
+ dname = dname .replace (os .sep , '/' )
525
+ offset = len (dname ) + 1
526
+
527
+ files = _get_files (pjoin (dname , pattern ))
528
+ for fname in files :
529
+ # Normalize the path.
530
+ root = os .path .dirname (fname )
531
+ full_path = '/' .join ([path , root [offset :]])
532
+ if full_path .endswith ('/' ):
533
+ full_path = full_path [:- 1 ]
534
+ file_data [full_path ].append (fname )
535
+
536
+ # Construct the data files spec.
537
+ data_files = []
538
+ for (path , files ) in file_data .items ():
539
+ data_files .append ((path , files ))
540
+ return data_files
541
+
542
+
500
543
def _get_files (file_patterns , top = HERE ):
501
544
"""Expand file patterns to a list of paths.
502
545
@@ -631,7 +674,7 @@ def _translate_glob_part(pat):
631
674
res = []
632
675
while i < n :
633
676
c = pat [i ]
634
- i = i + 1
677
+ i = i + 1
635
678
if c == '*' :
636
679
# Match anything but path separators:
637
680
res .append ('[^%s]*' % SEPARATORS )
@@ -640,16 +683,16 @@ def _translate_glob_part(pat):
640
683
elif c == '[' :
641
684
j = i
642
685
if j < n and pat [j ] == '!' :
643
- j = j + 1
686
+ j = j + 1
644
687
if j < n and pat [j ] == ']' :
645
- j = j + 1
688
+ j = j + 1
646
689
while j < n and pat [j ] != ']' :
647
- j = j + 1
690
+ j = j + 1
648
691
if j >= n :
649
692
res .append ('\\ [' )
650
693
else :
651
694
stuff = pat [i :j ].replace ('\\ ' , '\\ \\ ' )
652
- i = j + 1
695
+ i = j + 1
653
696
if stuff [0 ] == '!' :
654
697
stuff = '^' + stuff [1 :]
655
698
elif stuff [0 ] == '^' :
0 commit comments