~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Martin Pool
  • Date: 2006-06-20 07:55:43 UTC
  • mfrom: (1798 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1799.
  • Revision ID: mbp@sourcefrog.net-20060620075543-b10f6575d4a4fa32
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
77
77
    >>> i.path2id('')
78
78
    'TREE_ROOT'
79
79
    >>> i.add(InventoryDirectory('123', 'src', ROOT_ID))
80
 
    InventoryDirectory('123', 'src', parent_id='TREE_ROOT')
 
80
    InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None)
81
81
    >>> i.add(InventoryFile('2323', 'hello.c', parent_id='123'))
82
 
    InventoryFile('2323', 'hello.c', parent_id='123')
 
82
    InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None)
83
83
    >>> shouldbe = {0: 'src', 1: pathjoin('src','hello.c')}
84
84
    >>> for ix, j in enumerate(i.iter_entries()):
85
85
    ...   print (j[0] == shouldbe[ix], j[1])
86
86
    ... 
87
 
    (True, InventoryDirectory('123', 'src', parent_id='TREE_ROOT'))
88
 
    (True, InventoryFile('2323', 'hello.c', parent_id='123'))
 
87
    (True, InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None))
 
88
    (True, InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None))
89
89
    >>> i.add(InventoryFile('2323', 'bye.c', '123'))
90
90
    Traceback (most recent call last):
91
91
    ...
92
92
    BzrError: inventory already contains entry with id {2323}
93
93
    >>> i.add(InventoryFile('2324', 'bye.c', '123'))
94
 
    InventoryFile('2324', 'bye.c', parent_id='123')
 
94
    InventoryFile('2324', 'bye.c', parent_id='123', sha1=None, len=None)
95
95
    >>> i.add(InventoryDirectory('2325', 'wibble', '123'))
96
 
    InventoryDirectory('2325', 'wibble', parent_id='123')
 
96
    InventoryDirectory('2325', 'wibble', parent_id='123', revision=None)
97
97
    >>> i.path2id('src/wibble')
98
98
    '2325'
99
99
    >>> '2325' in i
100
100
    True
101
101
    >>> i.add(InventoryFile('2326', 'wibble.c', '2325'))
102
 
    InventoryFile('2326', 'wibble.c', parent_id='2325')
 
102
    InventoryFile('2326', 'wibble.c', parent_id='2325', sha1=None, len=None)
103
103
    >>> i['2326']
104
 
    InventoryFile('2326', 'wibble.c', parent_id='2325')
 
104
    InventoryFile('2326', 'wibble.c', parent_id='2325', sha1=None, len=None)
105
105
    >>> for path, entry in i.iter_entries():
106
106
    ...     print path
107
107
    ...     assert i.path2id(path)
123
123
    RENAMED = 'renamed'
124
124
    MODIFIED_AND_RENAMED = 'modified and renamed'
125
125
    
126
 
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
127
 
                 'text_id', 'parent_id', 'children', 'executable', 
128
 
                 'revision']
129
 
 
130
 
    def _add_text_to_weave(self, new_lines, parents, weave_store, transaction):
131
 
        versionedfile = weave_store.get_weave_or_empty(self.file_id,
132
 
                                                       transaction)
133
 
        versionedfile.add_lines(self.revision, parents, new_lines)
134
 
        versionedfile.clear_cache()
 
126
    __slots__ = []
135
127
 
136
128
    def detect_changes(self, old_entry):
137
129
        """Return a (text_modified, meta_modified) from this to old_entry.
166
158
                            versioned_file_store,
167
159
                            transaction,
168
160
                            entry_vf=None):
169
 
        """Return the revisions and entries that directly preceed this.
 
161
        """Return the revisions and entries that directly precede this.
170
162
 
171
163
        Returned as a map from revision to inventory entry.
172
164
 
325
317
        raise BzrError("don't know how to export {%s} of kind %r" % (self.file_id, self.kind))
326
318
 
327
319
    def sorted_children(self):
328
 
        l = self.children.items()
329
 
        l.sort()
330
 
        return l
 
320
        return sorted(self.children.items())
331
321
 
332
322
    @staticmethod
333
323
    def versionable_kind(kind):
347
337
        :param inv: Inventory from which the entry was loaded.
348
338
        :param tree: RevisionTree for this entry.
349
339
        """
350
 
        if self.parent_id != None:
 
340
        if self.parent_id is not None:
351
341
            if not inv.has_id(self.parent_id):
352
342
                raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
353
343
                        % (self.parent_id, rev_id))
403
393
        return 'unchanged'
404
394
 
405
395
    def __repr__(self):
406
 
        return ("%s(%r, %r, parent_id=%r)"
 
396
        return ("%s(%r, %r, parent_id=%r, revision=%r)"
407
397
                % (self.__class__.__name__,
408
398
                   self.file_id,
409
399
                   self.name,
410
 
                   self.parent_id))
 
400
                   self.parent_id,
 
401
                   self.revision))
411
402
 
412
403
    def snapshot(self, revision, path, previous_entries,
413
 
                 work_tree, weave_store, transaction):
 
404
                 work_tree, commit_builder):
414
405
        """Make a snapshot of this entry which may or may not have changed.
415
406
        
416
407
        This means that all its fields are populated, that it has its
418
409
        """
419
410
        mutter('new parents of %s are %r', path, previous_entries)
420
411
        self._read_tree_state(path, work_tree)
 
412
        # TODO: Where should we determine whether to reuse a
 
413
        # previous revision id or create a new revision? 20060606
421
414
        if len(previous_entries) == 1:
422
415
            # cannot be unchanged unless there is only one parent file rev.
423
416
            parent_ie = previous_entries.values()[0]
426
419
                self.revision = parent_ie.revision
427
420
                return "unchanged"
428
421
        return self._snapshot_into_revision(revision, previous_entries, 
429
 
                                            work_tree, weave_store, transaction)
 
422
                                            work_tree, commit_builder)
430
423
 
431
424
    def _snapshot_into_revision(self, revision, previous_entries, work_tree,
432
 
                                weave_store, transaction):
 
425
                                commit_builder):
433
426
        """Record this revision unconditionally into a store.
434
427
 
435
428
        The entry's last-changed revision property (`revision`) is updated to 
441
434
        """
442
435
        mutter('new revision {%s} for {%s}', revision, self.file_id)
443
436
        self.revision = revision
444
 
        self._snapshot_text(previous_entries, work_tree, weave_store,
445
 
                            transaction)
 
437
        self._snapshot_text(previous_entries, work_tree, commit_builder)
446
438
 
447
 
    def _snapshot_text(self, file_parents, work_tree, weave_store, transaction): 
 
439
    def _snapshot_text(self, file_parents, work_tree, commit_builder): 
448
440
        """Record the 'text' of this entry, whatever form that takes.
449
441
        
450
442
        This default implementation simply adds an empty text.
451
443
        """
452
 
        mutter('storing file {%s} in revision {%s}',
453
 
               self.file_id, self.revision)
454
 
        self._add_text_to_weave([], file_parents.keys(), weave_store, transaction)
 
444
        raise NotImplementedError(self._snapshot_text)
455
445
 
456
446
    def __eq__(self, other):
457
447
        if not isinstance(other, InventoryEntry):
478
468
    def _unchanged(self, previous_ie):
479
469
        """Has this entry changed relative to previous_ie.
480
470
 
481
 
        This method should be overriden in child classes.
 
471
        This method should be overridden in child classes.
482
472
        """
483
473
        compatible = True
484
474
        # different inv parent
506
496
 
507
497
class RootEntry(InventoryEntry):
508
498
 
 
499
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
500
                 'text_id', 'parent_id', 'children', 'executable', 
 
501
                 'revision', 'symlink_target']
 
502
 
509
503
    def _check(self, checker, rev_id, tree):
510
504
        """See InventoryEntry._check"""
511
505
 
515
509
        self.kind = 'root_directory'
516
510
        self.parent_id = None
517
511
        self.name = u''
 
512
        self.revision = None
518
513
 
519
514
    def __eq__(self, other):
520
515
        if not isinstance(other, RootEntry):
527
522
class InventoryDirectory(InventoryEntry):
528
523
    """A directory in an inventory."""
529
524
 
 
525
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
526
                 'text_id', 'parent_id', 'children', 'executable', 
 
527
                 'revision', 'symlink_target']
 
528
 
530
529
    def _check(self, checker, rev_id, tree):
531
530
        """See InventoryEntry._check"""
532
 
        if self.text_sha1 != None or self.text_size != None or self.text_id != None:
 
531
        if self.text_sha1 is not None or self.text_size is not None or self.text_id is not None:
533
532
            raise BzrCheckError('directory {%s} has text in revision {%s}'
534
533
                                % (self.file_id, rev_id))
535
534
 
562
561
        """See InventoryEntry._put_on_disk."""
563
562
        os.mkdir(fullpath)
564
563
 
 
564
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
 
565
        """See InventoryEntry._snapshot_text."""
 
566
        commit_builder.modified_directory(self.file_id, file_parents)
 
567
 
565
568
 
566
569
class InventoryFile(InventoryEntry):
567
570
    """A file in an inventory."""
568
571
 
 
572
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
573
                 'text_id', 'parent_id', 'children', 'executable', 
 
574
                 'revision', 'symlink_target']
 
575
 
569
576
    def _check(self, checker, tree_revision_id, tree):
570
577
        """See InventoryEntry._check"""
571
578
        t = (self.file_id, self.revision)
610
617
 
611
618
    def detect_changes(self, old_entry):
612
619
        """See InventoryEntry.detect_changes."""
613
 
        assert self.text_sha1 != None
614
 
        assert old_entry.text_sha1 != None
 
620
        assert self.text_sha1 is not None
 
621
        assert old_entry.text_sha1 is not None
615
622
        text_modified = (self.text_sha1 != old_entry.text_sha1)
616
623
        meta_modified = (self.executable != old_entry.executable)
617
624
        return text_modified, meta_modified
670
677
    def _read_tree_state(self, path, work_tree):
671
678
        """See InventoryEntry._read_tree_state."""
672
679
        self.text_sha1 = work_tree.get_file_sha1(self.file_id, path=path)
 
680
        # FIXME: 20050930 probe for the text size when getting sha1
 
681
        # in _read_tree_state
673
682
        self.executable = work_tree.is_executable(self.file_id, path=path)
674
683
 
 
684
    def __repr__(self):
 
685
        return ("%s(%r, %r, parent_id=%r, sha1=%r, len=%s)"
 
686
                % (self.__class__.__name__,
 
687
                   self.file_id,
 
688
                   self.name,
 
689
                   self.parent_id,
 
690
                   self.text_sha1,
 
691
                   self.text_size))
 
692
 
675
693
    def _forget_tree_state(self):
676
694
        self.text_sha1 = None
677
695
        self.executable = None
678
696
 
679
 
    def _snapshot_text(self, file_parents, work_tree, versionedfile_store, transaction):
 
697
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
680
698
        """See InventoryEntry._snapshot_text."""
681
 
        mutter('storing text of file {%s} in revision {%s} into %r',
682
 
               self.file_id, self.revision, versionedfile_store)
683
 
        # special case to avoid diffing on renames or 
684
 
        # reparenting
685
 
        if (len(file_parents) == 1
686
 
            and self.text_sha1 == file_parents.values()[0].text_sha1
687
 
            and self.text_size == file_parents.values()[0].text_size):
688
 
            previous_ie = file_parents.values()[0]
689
 
            versionedfile = versionedfile_store.get_weave(self.file_id, transaction)
690
 
            versionedfile.clone_text(self.revision, previous_ie.revision, file_parents.keys())
691
 
        else:
692
 
            new_lines = work_tree.get_file(self.file_id).readlines()
693
 
            self._add_text_to_weave(new_lines, file_parents.keys(), versionedfile_store,
694
 
                                    transaction)
695
 
            self.text_sha1 = sha_strings(new_lines)
696
 
            self.text_size = sum(map(len, new_lines))
697
 
 
 
699
        def get_content_byte_lines():
 
700
            return work_tree.get_file(self.file_id).readlines()
 
701
        self.text_sha1, self.text_size = commit_builder.modified_file_text(
 
702
            self.file_id, file_parents, get_content_byte_lines, self.text_sha1, self.text_size)
698
703
 
699
704
    def _unchanged(self, previous_ie):
700
705
        """See InventoryEntry._unchanged."""
713
718
class InventoryLink(InventoryEntry):
714
719
    """A file in an inventory."""
715
720
 
716
 
    __slots__ = ['symlink_target']
 
721
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
722
                 'text_id', 'parent_id', 'children', 'executable', 
 
723
                 'revision', 'symlink_target']
717
724
 
718
725
    def _check(self, checker, rev_id, tree):
719
726
        """See InventoryEntry._check"""
720
 
        if self.text_sha1 != None or self.text_size != None or self.text_id != None:
 
727
        if self.text_sha1 is not None or self.text_size is not None or self.text_id is not None:
721
728
            raise BzrCheckError('symlink {%s} has text in revision {%s}'
722
729
                    % (self.file_id, rev_id))
723
 
        if self.symlink_target == None:
 
730
        if self.symlink_target is None:
724
731
            raise BzrCheckError('symlink {%s} has no target in revision {%s}'
725
732
                    % (self.file_id, rev_id))
726
733
 
794
801
            compatible = False
795
802
        return compatible
796
803
 
 
804
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
 
805
        """See InventoryEntry._snapshot_text."""
 
806
        commit_builder.modified_link(
 
807
            self.file_id, file_parents, self.symlink_target)
 
808
 
797
809
 
798
810
class Inventory(object):
799
811
    """Inventory of versioned files in a tree.
814
826
 
815
827
    >>> inv = Inventory()
816
828
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
817
 
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT')
 
829
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT', sha1=None, len=None)
818
830
    >>> inv['123-123'].name
819
831
    'hello.c'
820
832
 
831
843
    [u'hello.c']
832
844
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
833
845
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
834
 
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678')
 
846
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678', sha1=None, len=None)
835
847
    """
836
848
    def __init__(self, root_id=ROOT_ID, revision_id=None):
837
849
        """Create or read an inventory.
848
860
        #if root_id is None:
849
861
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
850
862
        self.root = RootEntry(root_id)
 
863
        # FIXME: this isn't ever used, changing it to self.revision may break
 
864
        # things. TODO make everything use self.revision_id
851
865
        self.revision_id = revision_id
852
866
        self._byid = {self.root.file_id: self.root}
853
867
 
854
 
 
855
868
    def copy(self):
856
869
        # TODO: jam 20051218 Should copy also copy the revision_id?
857
870
        other = Inventory(self.root.file_id)
863
876
            other.add(entry.copy())
864
877
        return other
865
878
 
866
 
 
867
879
    def __iter__(self):
868
880
        return iter(self._byid)
869
881
 
870
 
 
871
882
    def __len__(self):
872
883
        """Returns number of entries."""
873
884
        return len(self._byid)
874
885
 
875
 
 
876
886
    def iter_entries(self, from_dir=None):
877
887
        """Return (path, entry) pairs, in order by name."""
878
 
        if from_dir == None:
 
888
        if from_dir is None:
879
889
            assert self.root
880
890
            from_dir = self.root
881
891
        elif isinstance(from_dir, basestring):
915
925
                # if we finished all children, pop it off the stack
916
926
                stack.pop()
917
927
 
 
928
    def iter_entries_by_dir(self, from_dir=None):
 
929
        """Iterate over the entries in a directory first order.
 
930
 
 
931
        This returns all entries for a directory before returning
 
932
        the entries for children of a directory. This is not
 
933
        lexicographically sorted order, and is a hybrid between
 
934
        depth-first and breadth-first.
 
935
 
 
936
        :return: This yields (path, entry) pairs
 
937
        """
 
938
        # TODO? Perhaps this should return the from_dir so that the root is
 
939
        # yielded? or maybe an option?
 
940
        if from_dir is None:
 
941
            assert self.root
 
942
            from_dir = self.root
 
943
        elif isinstance(from_dir, basestring):
 
944
            from_dir = self._byid[from_dir]
 
945
            
 
946
        stack = [(u'', from_dir)]
 
947
        while stack:
 
948
            cur_relpath, cur_dir = stack.pop()
 
949
 
 
950
            child_dirs = []
 
951
            for child_name, child_ie in sorted(cur_dir.children.iteritems()):
 
952
 
 
953
                child_relpath = cur_relpath + child_name
 
954
 
 
955
                yield child_relpath, child_ie
 
956
 
 
957
                if child_ie.kind == 'directory':
 
958
                    child_dirs.append((child_relpath+'/', child_ie))
 
959
            stack.extend(reversed(child_dirs))
 
960
 
918
961
    def entries(self):
919
962
        """Return list of (path, ie) for all entries except the root.
920
963
 
933
976
        descend(self.root, u'')
934
977
        return accum
935
978
 
936
 
 
937
979
    def directories(self):
938
980
        """Return (path, entry) pairs for all directories, including the root.
939
981
        """
950
992
        descend(self.root, u'')
951
993
        return accum
952
994
        
953
 
 
954
 
 
955
995
    def __contains__(self, file_id):
956
996
        """True if this entry contains a file with given id.
957
997
 
958
998
        >>> inv = Inventory()
959
999
        >>> inv.add(InventoryFile('123', 'foo.c', ROOT_ID))
960
 
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT')
 
1000
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT', sha1=None, len=None)
961
1001
        >>> '123' in inv
962
1002
        True
963
1003
        >>> '456' in inv
965
1005
        """
966
1006
        return file_id in self._byid
967
1007
 
968
 
 
969
1008
    def __getitem__(self, file_id):
970
1009
        """Return the entry for given file_id.
971
1010
 
972
1011
        >>> inv = Inventory()
973
1012
        >>> inv.add(InventoryFile('123123', 'hello.c', ROOT_ID))
974
 
        InventoryFile('123123', 'hello.c', parent_id='TREE_ROOT')
 
1013
        InventoryFile('123123', 'hello.c', parent_id='TREE_ROOT', sha1=None, len=None)
975
1014
        >>> inv['123123'].name
976
1015
        'hello.c'
977
1016
        """
978
1017
        try:
979
1018
            return self._byid[file_id]
980
1019
        except KeyError:
981
 
            if file_id == None:
 
1020
            if file_id is None:
982
1021
                raise BzrError("can't look up file_id None")
983
1022
            else:
984
1023
                raise BzrError("file_id {%s} not in inventory" % file_id)
985
1024
 
986
 
 
987
1025
    def get_file_kind(self, file_id):
988
1026
        return self._byid[file_id].kind
989
1027
 
990
1028
    def get_child(self, parent_id, filename):
991
1029
        return self[parent_id].children.get(filename)
992
1030
 
993
 
 
994
1031
    def add(self, entry):
995
1032
        """Add entry to inventory.
996
1033
 
1010
1047
        except KeyError:
1011
1048
            raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
1012
1049
 
1013
 
        if parent.children.has_key(entry.name):
 
1050
        if entry.name in parent.children:
1014
1051
            raise BzrError("%s is already versioned" %
1015
1052
                    pathjoin(self.id2path(parent.file_id), entry.name))
1016
1053
 
1018
1055
        parent.children[entry.name] = entry
1019
1056
        return entry
1020
1057
 
1021
 
 
1022
1058
    def add_path(self, relpath, kind, file_id=None, parent_id=None):
1023
1059
        """Add entry from a path.
1024
1060
 
1037
1073
        else:
1038
1074
            parent_path = parts[:-1]
1039
1075
            parent_id = self.path2id(parent_path)
1040
 
            if parent_id == None:
 
1076
            if parent_id is None:
1041
1077
                raise NotVersionedError(path=parent_path)
1042
1078
        ie = make_entry(kind, parts[-1], parent_id, file_id)
1043
1079
        return self.add(ie)
1047
1083
 
1048
1084
        >>> inv = Inventory()
1049
1085
        >>> inv.add(InventoryFile('123', 'foo.c', ROOT_ID))
1050
 
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT')
 
1086
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT', sha1=None, len=None)
1051
1087
        >>> '123' in inv
1052
1088
        True
1053
1089
        >>> del inv['123']
1063
1099
        if ie.parent_id is not None:
1064
1100
            del self[ie.parent_id].children[ie.name]
1065
1101
 
1066
 
 
1067
1102
    def __eq__(self, other):
1068
1103
        """Compare two sets by comparing their contents.
1069
1104
 
1072
1107
        >>> i1 == i2
1073
1108
        True
1074
1109
        >>> i1.add(InventoryFile('123', 'foo', ROOT_ID))
1075
 
        InventoryFile('123', 'foo', parent_id='TREE_ROOT')
 
1110
        InventoryFile('123', 'foo', parent_id='TREE_ROOT', sha1=None, len=None)
1076
1111
        >>> i1 == i2
1077
1112
        False
1078
1113
        >>> i2.add(InventoryFile('123', 'foo', ROOT_ID))
1079
 
        InventoryFile('123', 'foo', parent_id='TREE_ROOT')
 
1114
        InventoryFile('123', 'foo', parent_id='TREE_ROOT', sha1=None, len=None)
1080
1115
        >>> i1 == i2
1081
1116
        True
1082
1117
        """
1083
1118
        if not isinstance(other, Inventory):
1084
1119
            return NotImplemented
1085
1120
 
1086
 
        if len(self._byid) != len(other._byid):
1087
 
            # shortcut: obviously not the same
1088
 
            return False
1089
 
 
1090
1121
        return self._byid == other._byid
1091
1122
 
1092
 
 
1093
1123
    def __ne__(self, other):
1094
1124
        return not self.__eq__(other)
1095
1125
 
1096
 
 
1097
1126
    def __hash__(self):
1098
1127
        raise ValueError('not hashable')
1099
1128
 
1100
1129
    def _iter_file_id_parents(self, file_id):
1101
1130
        """Yield the parents of file_id up to the root."""
1102
 
        while file_id != None:
 
1131
        while file_id is not None:
1103
1132
            try:
1104
1133
                ie = self._byid[file_id]
1105
1134
            except KeyError:
1163
1192
 
1164
1193
        return parent.file_id
1165
1194
 
1166
 
 
1167
1195
    def has_filename(self, names):
1168
1196
        return bool(self.path2id(names))
1169
1197
 
1170
 
 
1171
1198
    def has_id(self, file_id):
1172
1199
        return self._byid.has_key(file_id)
1173
1200
 
1174
 
 
1175
1201
    def rename(self, file_id, new_parent_id, new_name):
1176
1202
        """Move a file within the inventory.
1177
1203
 
1227
1253
 
1228
1254
def is_valid_name(name):
1229
1255
    global _NAME_RE
1230
 
    if _NAME_RE == None:
 
1256
    if _NAME_RE is None:
1231
1257
        _NAME_RE = re.compile(r'^[^/\\]+$')
1232
1258
        
1233
1259
    return bool(_NAME_RE.match(name))