~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Vincent Ladeuil
  • Date: 2009-06-22 12:52:39 UTC
  • mto: (4471.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4472.
  • Revision ID: v.ladeuil+lp@free.fr-20090622125239-kabo9smxt9c3vnir
Use a consistent scheme for naming pyrex source files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from cStringIO import StringIO
18
17
import os
19
18
import re
20
19
import stat
35
34
                    splitdrive as _nt_splitdrive,
36
35
                    )
37
36
import posixpath
38
 
import sha
39
37
import shutil
40
38
from shutil import (
41
39
    rmtree,
42
40
    )
 
41
import subprocess
43
42
import tempfile
44
43
from tempfile import (
45
44
    mkdtemp,
53
52
    )
54
53
""")
55
54
 
 
55
# sha and md5 modules are deprecated in python2.6 but hashlib is available as
 
56
# of 2.5
 
57
if sys.version_info < (2, 5):
 
58
    import md5 as _mod_md5
 
59
    md5 = _mod_md5.new
 
60
    import sha as _mod_sha
 
61
    sha = _mod_sha.new
 
62
else:
 
63
    from hashlib import (
 
64
        md5,
 
65
        sha1 as sha,
 
66
        )
 
67
 
 
68
 
56
69
import bzrlib
57
70
from bzrlib import symbol_versioning
58
 
from bzrlib.symbol_versioning import (
59
 
    deprecated_function,
60
 
    )
61
 
from bzrlib.trace import mutter
62
71
 
63
72
 
64
73
# On win32, O_BINARY is used to indicate the file should
69
78
O_BINARY = getattr(os, 'O_BINARY', 0)
70
79
 
71
80
 
 
81
def get_unicode_argv():
 
82
    try:
 
83
        user_encoding = get_user_encoding()
 
84
        return [a.decode(user_encoding) for a in sys.argv[1:]]
 
85
    except UnicodeDecodeError:
 
86
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
 
87
                                                            "encoding." % a))
 
88
 
 
89
 
72
90
def make_readonly(filename):
73
91
    """Make a filename read-only."""
74
92
    mod = os.lstat(filename).st_mode
89
107
 
90
108
    :param paths: A container (and hence not None) of paths.
91
109
    :return: A set of paths sufficient to include everything in paths via
92
 
        is_inside_any, drawn from the paths parameter.
 
110
        is_inside, drawn from the paths parameter.
93
111
    """
94
 
    search_paths = set()
95
 
    paths = set(paths)
96
 
    for path in paths:
97
 
        other_paths = paths.difference([path])
98
 
        if not is_inside_any(other_paths, path):
99
 
            # this is a top level path, we must check it.
100
 
            search_paths.add(path)
101
 
    return search_paths
 
112
    if len(paths) < 2:
 
113
        return set(paths)
 
114
 
 
115
    def sort_key(path):
 
116
        return path.split('/')
 
117
    sorted_paths = sorted(list(paths), key=sort_key)
 
118
 
 
119
    search_paths = [sorted_paths[0]]
 
120
    for path in sorted_paths[1:]:
 
121
        if not is_inside(search_paths[-1], path):
 
122
            # This path is unique, add it
 
123
            search_paths.append(path)
 
124
 
 
125
    return set(search_paths)
102
126
 
103
127
 
104
128
_QUOTE_RE = None
113
137
    global _QUOTE_RE
114
138
    if _QUOTE_RE is None:
115
139
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
116
 
        
 
140
 
117
141
    if _QUOTE_RE.search(f):
118
142
        return '"' + f + '"'
119
143
    else:
122
146
 
123
147
_directory_kind = 'directory'
124
148
 
125
 
_formats = {
126
 
    stat.S_IFDIR:_directory_kind,
127
 
    stat.S_IFCHR:'chardev',
128
 
    stat.S_IFBLK:'block',
129
 
    stat.S_IFREG:'file',
130
 
    stat.S_IFIFO:'fifo',
131
 
    stat.S_IFLNK:'symlink',
132
 
    stat.S_IFSOCK:'socket',
133
 
}
134
 
 
135
 
 
136
 
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
137
 
    """Generate a file kind from a stat mode. This is used in walkdirs.
138
 
 
139
 
    Its performance is critical: Do not mutate without careful benchmarking.
140
 
    """
141
 
    try:
142
 
        return _formats[stat_mode & 0170000]
143
 
    except KeyError:
144
 
        return _unknown
145
 
 
146
 
 
147
 
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
148
 
    try:
149
 
        return _mapper(_lstat(f).st_mode)
150
 
    except OSError, e:
151
 
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
152
 
            raise errors.NoSuchFile(f)
153
 
        raise
154
 
 
155
 
 
156
149
def get_umask():
157
150
    """Return the current umask"""
158
151
    # Assume that people aren't messing with the umask while running
194
187
 
195
188
def fancy_rename(old, new, rename_func, unlink_func):
196
189
    """A fancy rename, when you don't have atomic rename.
197
 
    
 
190
 
198
191
    :param old: The old path, to rename from
199
192
    :param new: The new path, to rename to
200
193
    :param rename_func: The potentially non-atomic rename function
202
195
    """
203
196
 
204
197
    # sftp rename doesn't allow overwriting, so play tricks:
205
 
    import random
206
198
    base = os.path.basename(new)
207
199
    dirname = os.path.dirname(new)
208
200
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
315
307
        path = cwd + '\\' + path
316
308
    return _win32_fixdrive(_nt_normpath(path).replace('\\', '/'))
317
309
 
318
 
if win32utils.winver == 'Windows 98':
319
 
    _win32_abspath = _win98_abspath
320
 
 
321
310
 
322
311
def _win32_realpath(path):
323
312
    # Real _nt_realpath doesn't have a problem with a unicode cwd
344
333
    """We expect to be able to atomically replace 'new' with old.
345
334
 
346
335
    On win32, if new exists, it must be moved out of the way first,
347
 
    and then deleted. 
 
336
    and then deleted.
348
337
    """
349
338
    try:
350
339
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
351
340
    except OSError, e:
352
341
        if e.errno in (errno.EPERM, errno.EACCES, errno.EBUSY, errno.EINVAL):
353
 
            # If we try to rename a non-existant file onto cwd, we get 
354
 
            # EPERM or EACCES instead of ENOENT, this will raise ENOENT 
 
342
            # If we try to rename a non-existant file onto cwd, we get
 
343
            # EPERM or EACCES instead of ENOENT, this will raise ENOENT
355
344
            # if the old path doesn't exist, sometimes we get EACCES
356
345
            # On Linux, we seem to get EBUSY, on Mac we get EINVAL
357
346
            os.lstat(old)
382
371
 
383
372
 
384
373
if sys.platform == 'win32':
385
 
    abspath = _win32_abspath
 
374
    if win32utils.winver == 'Windows 98':
 
375
        abspath = _win98_abspath
 
376
    else:
 
377
        abspath = _win32_abspath
386
378
    realpath = _win32_realpath
387
379
    pathjoin = _win32_pathjoin
388
380
    normpath = _win32_normpath
408
400
    def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
409
401
        """Replacer for shutil.rmtree: could remove readonly dirs/files"""
410
402
        return shutil.rmtree(path, ignore_errors, onerror)
 
403
 
 
404
    f = win32utils.get_unicode_argv     # special function or None
 
405
    if f is not None:
 
406
        get_unicode_argv = f
 
407
 
411
408
elif sys.platform == 'darwin':
412
409
    getcwd = _mac_getcwd
413
410
 
417
414
 
418
415
    This attempts to check both sys.stdout and sys.stdin to see
419
416
    what encoding they are in, and if that fails it falls back to
420
 
    bzrlib.user_encoding.
 
417
    osutils.get_user_encoding().
421
418
    The problem is that on Windows, locale.getpreferredencoding()
422
419
    is not the same encoding as that used by the console:
423
420
    http://mail.python.org/pipermail/python-list/2003-May/162357.html
425
422
    On my standard US Windows XP, the preferred encoding is
426
423
    cp1252, but the console is cp437
427
424
    """
 
425
    from bzrlib.trace import mutter
428
426
    output_encoding = getattr(sys.stdout, 'encoding', None)
429
427
    if not output_encoding:
430
428
        input_encoding = getattr(sys.stdin, 'encoding', None)
431
429
        if not input_encoding:
432
 
            output_encoding = bzrlib.user_encoding
433
 
            mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
430
            output_encoding = get_user_encoding()
 
431
            mutter('encoding stdout as osutils.get_user_encoding() %r',
 
432
                   output_encoding)
434
433
        else:
435
434
            output_encoding = input_encoding
436
435
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
438
437
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
439
438
    if output_encoding == 'cp0':
440
439
        # invalid encoding (cp0 means 'no codepage' on Windows)
441
 
        output_encoding = bzrlib.user_encoding
 
440
        output_encoding = get_user_encoding()
442
441
        mutter('cp0 is invalid encoding.'
443
 
               ' encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
442
               ' encoding stdout as osutils.get_user_encoding() %r',
 
443
               output_encoding)
444
444
    # check encoding
445
445
    try:
446
446
        codecs.lookup(output_encoding)
448
448
        sys.stderr.write('bzr: warning:'
449
449
                         ' unknown terminal encoding %s.\n'
450
450
                         '  Using encoding %s instead.\n'
451
 
                         % (output_encoding, bzrlib.user_encoding)
 
451
                         % (output_encoding, get_user_encoding())
452
452
                        )
453
 
        output_encoding = bzrlib.user_encoding
 
453
        output_encoding = get_user_encoding()
454
454
 
455
455
    return output_encoding
456
456
 
491
491
 
492
492
def is_inside(dir, fname):
493
493
    """True if fname is inside dir.
494
 
    
 
494
 
495
495
    The parameters should typically be passed to osutils.normpath first, so
496
496
    that . and .. and repeated slashes are eliminated, and the separators
497
497
    are canonical for the platform.
498
 
    
499
 
    The empty string as a dir name is taken as top-of-tree and matches 
 
498
 
 
499
    The empty string as a dir name is taken as top-of-tree and matches
500
500
    everything.
501
501
    """
502
 
    # XXX: Most callers of this can actually do something smarter by 
 
502
    # XXX: Most callers of this can actually do something smarter by
503
503
    # looking at the inventory
504
504
    if dir == fname:
505
505
        return True
506
 
    
 
506
 
507
507
    if dir == '':
508
508
        return True
509
509
 
529
529
    return False
530
530
 
531
531
 
532
 
def pumpfile(fromfile, tofile):
 
532
def pumpfile(from_file, to_file, read_length=-1, buff_size=32768,
 
533
             report_activity=None, direction='read'):
533
534
    """Copy contents of one file to another.
534
 
    
 
535
 
 
536
    The read_length can either be -1 to read to end-of-file (EOF) or
 
537
    it can specify the maximum number of bytes to read.
 
538
 
 
539
    The buff_size represents the maximum size for each read operation
 
540
    performed on from_file.
 
541
 
 
542
    :param report_activity: Call this as bytes are read, see
 
543
        Transport._report_activity
 
544
    :param direction: Will be passed to report_activity
 
545
 
535
546
    :return: The number of bytes copied.
536
547
    """
537
 
    BUFSIZE = 32768
538
548
    length = 0
539
 
    while True:
540
 
        b = fromfile.read(BUFSIZE)
541
 
        if not b:
542
 
            break
543
 
        tofile.write(b)
544
 
        length += len(b)
 
549
    if read_length >= 0:
 
550
        # read specified number of bytes
 
551
 
 
552
        while read_length > 0:
 
553
            num_bytes_to_read = min(read_length, buff_size)
 
554
 
 
555
            block = from_file.read(num_bytes_to_read)
 
556
            if not block:
 
557
                # EOF reached
 
558
                break
 
559
            if report_activity is not None:
 
560
                report_activity(len(block), direction)
 
561
            to_file.write(block)
 
562
 
 
563
            actual_bytes_read = len(block)
 
564
            read_length -= actual_bytes_read
 
565
            length += actual_bytes_read
 
566
    else:
 
567
        # read to EOF
 
568
        while True:
 
569
            block = from_file.read(buff_size)
 
570
            if not block:
 
571
                # EOF reached
 
572
                break
 
573
            if report_activity is not None:
 
574
                report_activity(len(block), direction)
 
575
            to_file.write(block)
 
576
            length += len(block)
545
577
    return length
546
578
 
547
579
 
 
580
def pump_string_file(bytes, file_handle, segment_size=None):
 
581
    """Write bytes to file_handle in many smaller writes.
 
582
 
 
583
    :param bytes: The string to write.
 
584
    :param file_handle: The file to write to.
 
585
    """
 
586
    # Write data in chunks rather than all at once, because very large
 
587
    # writes fail on some platforms (e.g. Windows with SMB  mounted
 
588
    # drives).
 
589
    if not segment_size:
 
590
        segment_size = 5242880 # 5MB
 
591
    segments = range(len(bytes) / segment_size + 1)
 
592
    write = file_handle.write
 
593
    for segment_index in segments:
 
594
        segment = buffer(bytes, segment_index * segment_size, segment_size)
 
595
        write(segment)
 
596
 
 
597
 
548
598
def file_iterator(input_file, readsize=32768):
549
599
    while True:
550
600
        b = input_file.read(readsize)
558
608
 
559
609
    The file cursor should be already at the start.
560
610
    """
561
 
    s = sha.new()
 
611
    s = sha()
562
612
    BUFSIZE = 128<<10
563
613
    while True:
564
614
        b = f.read(BUFSIZE)
568
618
    return s.hexdigest()
569
619
 
570
620
 
 
621
def size_sha_file(f):
 
622
    """Calculate the size and hexdigest of an open file.
 
623
 
 
624
    The file cursor should be already at the start and
 
625
    the caller is responsible for closing the file afterwards.
 
626
    """
 
627
    size = 0
 
628
    s = sha()
 
629
    BUFSIZE = 128<<10
 
630
    while True:
 
631
        b = f.read(BUFSIZE)
 
632
        if not b:
 
633
            break
 
634
        size += len(b)
 
635
        s.update(b)
 
636
    return size, s.hexdigest()
 
637
 
 
638
 
571
639
def sha_file_by_name(fname):
572
640
    """Calculate the SHA1 of a file by reading the full text"""
573
 
    s = sha.new()
 
641
    s = sha()
574
642
    f = os.open(fname, os.O_RDONLY | O_BINARY)
575
643
    try:
576
644
        while True:
582
650
        os.close(f)
583
651
 
584
652
 
585
 
def sha_strings(strings, _factory=sha.new):
 
653
def sha_strings(strings, _factory=sha):
586
654
    """Return the sha-1 of concatenation of strings"""
587
655
    s = _factory()
588
656
    map(s.update, strings)
589
657
    return s.hexdigest()
590
658
 
591
659
 
592
 
def sha_string(f, _factory=sha.new):
 
660
def sha_string(f, _factory=sha):
593
661
    return _factory(f).hexdigest()
594
662
 
595
663
 
596
664
def fingerprint_file(f):
597
665
    b = f.read()
598
666
    return {'size': len(b),
599
 
            'sha1': sha.new(b).hexdigest()}
 
667
            'sha1': sha(b).hexdigest()}
600
668
 
601
669
 
602
670
def compare_files(a, b):
618
686
    offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
619
687
    return offset.days * 86400 + offset.seconds
620
688
 
621
 
    
 
689
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
 
690
 
622
691
def format_date(t, offset=0, timezone='original', date_fmt=None,
623
692
                show_offset=True):
624
693
    """Return a formatted date string.
628
697
    :param timezone: How to display the time: 'utc', 'original' for the
629
698
         timezone specified by offset, or 'local' for the process's current
630
699
         timezone.
631
 
    :param show_offset: Whether to append the timezone.
632
 
    :param date_fmt: strftime format.
633
 
    """
 
700
    :param date_fmt: strftime format.
 
701
    :param show_offset: Whether to append the timezone.
 
702
    """
 
703
    (date_fmt, tt, offset_str) = \
 
704
               _format_date(t, offset, timezone, date_fmt, show_offset)
 
705
    date_fmt = date_fmt.replace('%a', weekdays[tt[6]])
 
706
    date_str = time.strftime(date_fmt, tt)
 
707
    return date_str + offset_str
 
708
 
 
709
def format_local_date(t, offset=0, timezone='original', date_fmt=None,
 
710
                      show_offset=True):
 
711
    """Return an unicode date string formatted according to the current locale.
 
712
 
 
713
    :param t: Seconds since the epoch.
 
714
    :param offset: Timezone offset in seconds east of utc.
 
715
    :param timezone: How to display the time: 'utc', 'original' for the
 
716
         timezone specified by offset, or 'local' for the process's current
 
717
         timezone.
 
718
    :param date_fmt: strftime format.
 
719
    :param show_offset: Whether to append the timezone.
 
720
    """
 
721
    (date_fmt, tt, offset_str) = \
 
722
               _format_date(t, offset, timezone, date_fmt, show_offset)
 
723
    date_str = time.strftime(date_fmt, tt)
 
724
    if not isinstance(date_str, unicode):
 
725
        date_str = date_str.decode(get_user_encoding(), 'replace')
 
726
    return date_str + offset_str
 
727
 
 
728
def _format_date(t, offset, timezone, date_fmt, show_offset):
634
729
    if timezone == 'utc':
635
730
        tt = time.gmtime(t)
636
731
        offset = 0
649
744
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
650
745
    else:
651
746
        offset_str = ''
652
 
    return (time.strftime(date_fmt, tt) +  offset_str)
 
747
    return (date_fmt, tt, offset_str)
653
748
 
654
749
 
655
750
def compact_date(when):
656
751
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
657
 
    
 
752
 
658
753
 
659
754
def format_delta(delta):
660
755
    """Get a nice looking string for a time delta.
736
831
ALNUM = '0123456789abcdefghijklmnopqrstuvwxyz'
737
832
def rand_chars(num):
738
833
    """Return a random string of num alphanumeric characters
739
 
    
740
 
    The result only contains lowercase chars because it may be used on 
 
834
 
 
835
    The result only contains lowercase chars because it may be used on
741
836
    case-insensitive filesystems.
742
837
    """
743
838
    s = ''
765
860
            rps.append(f)
766
861
    return rps
767
862
 
 
863
 
768
864
def joinpath(p):
769
865
    for f in p:
770
866
        if (f == '..') or (f is None) or (f == ''):
772
868
    return pathjoin(*p)
773
869
 
774
870
 
 
871
def parent_directories(filename):
 
872
    """Return the list of parent directories, deepest first.
 
873
    
 
874
    For example, parent_directories("a/b/c") -> ["a/b", "a"].
 
875
    """
 
876
    parents = []
 
877
    parts = splitpath(dirname(filename))
 
878
    while parts:
 
879
        parents.append(joinpath(parts))
 
880
        parts.pop()
 
881
    return parents
 
882
 
 
883
 
 
884
try:
 
885
    from bzrlib._chunks_to_lines_pyx import chunks_to_lines
 
886
except ImportError:
 
887
    from bzrlib._chunks_to_lines_py import chunks_to_lines
 
888
 
 
889
 
775
890
def split_lines(s):
776
891
    """Split s into lines, but without removing the newline characters."""
 
892
    # Trivially convert a fulltext into a 'chunked' representation, and let
 
893
    # chunks_to_lines do the heavy lifting.
 
894
    if isinstance(s, str):
 
895
        # chunks_to_lines only supports 8-bit strings
 
896
        return chunks_to_lines([s])
 
897
    else:
 
898
        return _split_lines(s)
 
899
 
 
900
 
 
901
def _split_lines(s):
 
902
    """Split s into lines, but without removing the newline characters.
 
903
 
 
904
    This supports Unicode or plain string objects.
 
905
    """
777
906
    lines = s.split('\n')
778
907
    result = [line + '\n' for line in lines[:-1]]
779
908
    if lines[-1]:
825
954
        return False
826
955
 
827
956
 
 
957
def host_os_dereferences_symlinks():
 
958
    return (has_symlinks()
 
959
            and sys.platform not in ('cygwin', 'win32'))
 
960
 
 
961
 
 
962
def readlink(abspath):
 
963
    """Return a string representing the path to which the symbolic link points.
 
964
 
 
965
    :param abspath: The link absolute unicode path.
 
966
 
 
967
    This his guaranteed to return the symbolic link in unicode in all python
 
968
    versions.
 
969
    """
 
970
    link = abspath.encode(_fs_enc)
 
971
    target = os.readlink(link)
 
972
    target = target.decode(_fs_enc)
 
973
    return target
 
974
 
 
975
 
828
976
def contains_whitespace(s):
829
977
    """True if there are any whitespace characters in s."""
830
978
    # string.whitespace can include '\xa0' in certain locales, because it is
889
1037
        return ''
890
1038
 
891
1039
 
 
1040
def _cicp_canonical_relpath(base, path):
 
1041
    """Return the canonical path relative to base.
 
1042
 
 
1043
    Like relpath, but on case-insensitive-case-preserving file-systems, this
 
1044
    will return the relpath as stored on the file-system rather than in the
 
1045
    case specified in the input string, for all existing portions of the path.
 
1046
 
 
1047
    This will cause O(N) behaviour if called for every path in a tree; if you
 
1048
    have a number of paths to convert, you should use canonical_relpaths().
 
1049
    """
 
1050
    # TODO: it should be possible to optimize this for Windows by using the
 
1051
    # win32 API FindFiles function to look for the specified name - but using
 
1052
    # os.listdir() still gives us the correct, platform agnostic semantics in
 
1053
    # the short term.
 
1054
 
 
1055
    rel = relpath(base, path)
 
1056
    # '.' will have been turned into ''
 
1057
    if not rel:
 
1058
        return rel
 
1059
 
 
1060
    abs_base = abspath(base)
 
1061
    current = abs_base
 
1062
    _listdir = os.listdir
 
1063
 
 
1064
    # use an explicit iterator so we can easily consume the rest on early exit.
 
1065
    bit_iter = iter(rel.split('/'))
 
1066
    for bit in bit_iter:
 
1067
        lbit = bit.lower()
 
1068
        for look in _listdir(current):
 
1069
            if lbit == look.lower():
 
1070
                current = pathjoin(current, look)
 
1071
                break
 
1072
        else:
 
1073
            # got to the end, nothing matched, so we just return the
 
1074
            # non-existing bits as they were specified (the filename may be
 
1075
            # the target of a move, for example).
 
1076
            current = pathjoin(current, bit, *list(bit_iter))
 
1077
            break
 
1078
    return current[len(abs_base)+1:]
 
1079
 
 
1080
# XXX - TODO - we need better detection/integration of case-insensitive
 
1081
# file-systems; Linux often sees FAT32 devices (or NFS-mounted OSX
 
1082
# filesystems), for example, so could probably benefit from the same basic
 
1083
# support there.  For now though, only Windows and OSX get that support, and
 
1084
# they get it for *all* file-systems!
 
1085
if sys.platform in ('win32', 'darwin'):
 
1086
    canonical_relpath = _cicp_canonical_relpath
 
1087
else:
 
1088
    canonical_relpath = relpath
 
1089
 
 
1090
def canonical_relpaths(base, paths):
 
1091
    """Create an iterable to canonicalize a sequence of relative paths.
 
1092
 
 
1093
    The intent is for this implementation to use a cache, vastly speeding
 
1094
    up multiple transformations in the same directory.
 
1095
    """
 
1096
    # but for now, we haven't optimized...
 
1097
    return [canonical_relpath(base, p) for p in paths]
 
1098
 
892
1099
def safe_unicode(unicode_or_utf8_string):
893
1100
    """Coerce unicode_or_utf8_string into unicode.
894
1101
 
895
1102
    If it is unicode, it is returned.
896
 
    Otherwise it is decoded from utf-8. If a decoding error
897
 
    occurs, it is wrapped as a If the decoding fails, the exception is wrapped 
898
 
    as a BzrBadParameter exception.
 
1103
    Otherwise it is decoded from utf-8. If decoding fails, the exception is
 
1104
    wrapped in a BzrBadParameterNotUnicode exception.
899
1105
    """
900
1106
    if isinstance(unicode_or_utf8_string, unicode):
901
1107
        return unicode_or_utf8_string
988
1194
 
989
1195
    On platforms where the system normalizes filenames (Mac OSX),
990
1196
    you can access a file by any path which will normalize correctly.
991
 
    On platforms where the system does not normalize filenames 
 
1197
    On platforms where the system does not normalize filenames
992
1198
    (Windows, Linux), you have to access a file by its exact path.
993
1199
 
994
 
    Internally, bzr only supports NFC normalization, since that is 
 
1200
    Internally, bzr only supports NFC normalization, since that is
995
1201
    the standard for XML documents.
996
1202
 
997
1203
    So return the normalized path, and a flag indicating if the file
1068
1274
            del os.environ[env_variable]
1069
1275
    else:
1070
1276
        if isinstance(value, unicode):
1071
 
            value = value.encode(bzrlib.user_encoding)
 
1277
            value = value.encode(get_user_encoding())
1072
1278
        os.environ[env_variable] = value
1073
1279
    return orig_val
1074
1280
 
1077
1283
 
1078
1284
 
1079
1285
def check_legal_path(path):
1080
 
    """Check whether the supplied path is legal.  
 
1286
    """Check whether the supplied path is legal.
1081
1287
    This is only required on Windows, so we don't test on other platforms
1082
1288
    right now.
1083
1289
    """
1087
1293
        raise errors.IllegalPath(path)
1088
1294
 
1089
1295
 
 
1296
_WIN32_ERROR_DIRECTORY = 267 # Similar to errno.ENOTDIR
 
1297
 
 
1298
def _is_error_enotdir(e):
 
1299
    """Check if this exception represents ENOTDIR.
 
1300
 
 
1301
    Unfortunately, python is very inconsistent about the exception
 
1302
    here. The cases are:
 
1303
      1) Linux, Mac OSX all versions seem to set errno == ENOTDIR
 
1304
      2) Windows, Python2.4, uses errno == ERROR_DIRECTORY (267)
 
1305
         which is the windows error code.
 
1306
      3) Windows, Python2.5 uses errno == EINVAL and
 
1307
         winerror == ERROR_DIRECTORY
 
1308
 
 
1309
    :param e: An Exception object (expected to be OSError with an errno
 
1310
        attribute, but we should be able to cope with anything)
 
1311
    :return: True if this represents an ENOTDIR error. False otherwise.
 
1312
    """
 
1313
    en = getattr(e, 'errno', None)
 
1314
    if (en == errno.ENOTDIR
 
1315
        or (sys.platform == 'win32'
 
1316
            and (en == _WIN32_ERROR_DIRECTORY
 
1317
                 or (en == errno.EINVAL
 
1318
                     and getattr(e, 'winerror', None) == _WIN32_ERROR_DIRECTORY)
 
1319
        ))):
 
1320
        return True
 
1321
    return False
 
1322
 
 
1323
 
1090
1324
def walkdirs(top, prefix=""):
1091
1325
    """Yield data about all the directories in a tree.
1092
 
    
 
1326
 
1093
1327
    This yields all the data about the contents of a directory at a time.
1094
1328
    After each directory has been yielded, if the caller has mutated the list
1095
1329
    to exclude some directories, they are then not descended into.
1096
 
    
 
1330
 
1097
1331
    The data yielded is of the form:
1098
1332
    ((directory-relpath, directory-path-from-top),
1099
1333
    [(relpath, basename, kind, lstat, path-from-top), ...]),
1100
1334
     - directory-relpath is the relative path of the directory being returned
1101
1335
       with respect to top. prefix is prepended to this.
1102
 
     - directory-path-from-root is the path including top for this directory. 
 
1336
     - directory-path-from-root is the path including top for this directory.
1103
1337
       It is suitable for use with os functions.
1104
1338
     - relpath is the relative path within the subtree being walked.
1105
1339
     - basename is the basename of the path
1107
1341
       present within the tree - but it may be recorded as versioned. See
1108
1342
       versioned_kind.
1109
1343
     - lstat is the stat data *if* the file was statted.
1110
 
     - planned, not implemented: 
 
1344
     - planned, not implemented:
1111
1345
       path_from_tree_root is the path from the root of the tree.
1112
1346
 
1113
 
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This 
 
1347
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This
1114
1348
        allows one to walk a subtree but get paths that are relative to a tree
1115
1349
        rooted higher up.
1116
1350
    :return: an iterator over the dirs.
1117
1351
    """
1118
1352
    #TODO there is a bit of a smell where the results of the directory-
1119
 
    # summary in this, and the path from the root, may not agree 
 
1353
    # summary in this, and the path from the root, may not agree
1120
1354
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
1121
1355
    # potentially confusing output. We should make this more robust - but
1122
1356
    # not at a speed cost. RBC 20060731
1123
1357
    _lstat = os.lstat
1124
1358
    _directory = _directory_kind
1125
1359
    _listdir = os.listdir
1126
 
    _kind_from_mode = _formats.get
 
1360
    _kind_from_mode = file_kind_from_stat_mode
1127
1361
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1128
1362
    while pending:
1129
1363
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1136
1370
 
1137
1371
        dirblock = []
1138
1372
        append = dirblock.append
1139
 
        for name in sorted(_listdir(top)):
1140
 
            abspath = top_slash + name
1141
 
            statvalue = _lstat(abspath)
1142
 
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1143
 
            append((relprefix + name, name, kind, statvalue, abspath))
 
1373
        try:
 
1374
            names = sorted(_listdir(top))
 
1375
        except OSError, e:
 
1376
            if not _is_error_enotdir(e):
 
1377
                raise
 
1378
        else:
 
1379
            for name in names:
 
1380
                abspath = top_slash + name
 
1381
                statvalue = _lstat(abspath)
 
1382
                kind = _kind_from_mode(statvalue.st_mode)
 
1383
                append((relprefix + name, name, kind, statvalue, abspath))
1144
1384
        yield (relroot, top), dirblock
1145
1385
 
1146
1386
        # push the user specified dirs from dirblock
1147
1387
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1148
1388
 
1149
1389
 
 
1390
class DirReader(object):
 
1391
    """An interface for reading directories."""
 
1392
 
 
1393
    def top_prefix_to_starting_dir(self, top, prefix=""):
 
1394
        """Converts top and prefix to a starting dir entry
 
1395
 
 
1396
        :param top: A utf8 path
 
1397
        :param prefix: An optional utf8 path to prefix output relative paths
 
1398
            with.
 
1399
        :return: A tuple starting with prefix, and ending with the native
 
1400
            encoding of top.
 
1401
        """
 
1402
        raise NotImplementedError(self.top_prefix_to_starting_dir)
 
1403
 
 
1404
    def read_dir(self, prefix, top):
 
1405
        """Read a specific dir.
 
1406
 
 
1407
        :param prefix: A utf8 prefix to be preprended to the path basenames.
 
1408
        :param top: A natively encoded path to read.
 
1409
        :return: A list of the directories contents. Each item contains:
 
1410
            (utf8_relpath, utf8_name, kind, lstatvalue, native_abspath)
 
1411
        """
 
1412
        raise NotImplementedError(self.read_dir)
 
1413
 
 
1414
 
 
1415
_selected_dir_reader = None
 
1416
 
 
1417
 
1150
1418
def _walkdirs_utf8(top, prefix=""):
1151
1419
    """Yield data about all the directories in a tree.
1152
1420
 
1161
1429
        path-from-top might be unicode or utf8, but it is the correct path to
1162
1430
        pass to os functions to affect the file in question. (such as os.lstat)
1163
1431
    """
1164
 
    fs_encoding = _fs_enc.upper()
1165
 
    if (sys.platform == 'win32' or
1166
 
        fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968')): # ascii
1167
 
        return _walkdirs_unicode_to_utf8(top, prefix=prefix)
1168
 
    else:
1169
 
        return _walkdirs_fs_utf8(top, prefix=prefix)
1170
 
 
1171
 
 
1172
 
def _walkdirs_fs_utf8(top, prefix=""):
1173
 
    """See _walkdirs_utf8.
1174
 
 
1175
 
    This sub-function is called when we know the filesystem is already in utf8
1176
 
    encoding. So we don't need to transcode filenames.
1177
 
    """
1178
 
    _lstat = os.lstat
1179
 
    _directory = _directory_kind
1180
 
    _listdir = os.listdir
1181
 
    _kind_from_mode = _formats.get
 
1432
    global _selected_dir_reader
 
1433
    if _selected_dir_reader is None:
 
1434
        fs_encoding = _fs_enc.upper()
 
1435
        if sys.platform == "win32" and win32utils.winver == 'Windows NT':
 
1436
            # Win98 doesn't have unicode apis like FindFirstFileW
 
1437
            # TODO: We possibly could support Win98 by falling back to the
 
1438
            #       original FindFirstFile, and using TCHAR instead of WCHAR,
 
1439
            #       but that gets a bit tricky, and requires custom compiling
 
1440
            #       for win98 anyway.
 
1441
            try:
 
1442
                from bzrlib._walkdirs_win32 import Win32ReadDir
 
1443
                _selected_dir_reader = Win32ReadDir()
 
1444
            except ImportError:
 
1445
                pass
 
1446
        elif fs_encoding in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
 
1447
            # ANSI_X3.4-1968 is a form of ASCII
 
1448
            try:
 
1449
                from bzrlib._readdir_pyx import UTF8DirReader
 
1450
                _selected_dir_reader = UTF8DirReader()
 
1451
            except ImportError:
 
1452
                pass
 
1453
 
 
1454
    if _selected_dir_reader is None:
 
1455
        # Fallback to the python version
 
1456
        _selected_dir_reader = UnicodeDirReader()
1182
1457
 
1183
1458
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1184
1459
    # But we don't actually uses 1-3 in pending, so set them to None
1185
 
    pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
 
1460
    pending = [[_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]]
 
1461
    read_dir = _selected_dir_reader.read_dir
 
1462
    _directory = _directory_kind
1186
1463
    while pending:
1187
 
        relroot, _, _, _, top = pending.pop()
1188
 
        if relroot:
1189
 
            relprefix = relroot + '/'
1190
 
        else:
1191
 
            relprefix = ''
1192
 
        top_slash = top + '/'
1193
 
 
1194
 
        dirblock = []
1195
 
        append = dirblock.append
1196
 
        for name in sorted(_listdir(top)):
1197
 
            abspath = top_slash + name
1198
 
            statvalue = _lstat(abspath)
1199
 
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1200
 
            append((relprefix + name, name, kind, statvalue, abspath))
 
1464
        relroot, _, _, _, top = pending[-1].pop()
 
1465
        if not pending[-1]:
 
1466
            pending.pop()
 
1467
        dirblock = sorted(read_dir(relroot, top))
1201
1468
        yield (relroot, top), dirblock
1202
 
 
1203
1469
        # push the user specified dirs from dirblock
1204
 
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1205
 
 
1206
 
 
1207
 
def _walkdirs_unicode_to_utf8(top, prefix=""):
1208
 
    """See _walkdirs_utf8
1209
 
 
1210
 
    Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
1211
 
    Unicode paths.
1212
 
    This is currently the fallback code path when the filesystem encoding is
1213
 
    not UTF-8. It may be better to implement an alternative so that we can
1214
 
    safely handle paths that are not properly decodable in the current
1215
 
    encoding.
1216
 
    """
1217
 
    _utf8_encode = codecs.getencoder('utf8')
1218
 
    _lstat = os.lstat
1219
 
    _directory = _directory_kind
1220
 
    _listdir = os.listdir
1221
 
    _kind_from_mode = _formats.get
1222
 
 
1223
 
    pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
1224
 
    while pending:
1225
 
        relroot, _, _, _, top = pending.pop()
1226
 
        if relroot:
1227
 
            relprefix = relroot + '/'
 
1470
        next = [d for d in reversed(dirblock) if d[2] == _directory]
 
1471
        if next:
 
1472
            pending.append(next)
 
1473
 
 
1474
 
 
1475
class UnicodeDirReader(DirReader):
 
1476
    """A dir reader for non-utf8 file systems, which transcodes."""
 
1477
 
 
1478
    __slots__ = ['_utf8_encode']
 
1479
 
 
1480
    def __init__(self):
 
1481
        self._utf8_encode = codecs.getencoder('utf8')
 
1482
 
 
1483
    def top_prefix_to_starting_dir(self, top, prefix=""):
 
1484
        """See DirReader.top_prefix_to_starting_dir."""
 
1485
        return (safe_utf8(prefix), None, None, None, safe_unicode(top))
 
1486
 
 
1487
    def read_dir(self, prefix, top):
 
1488
        """Read a single directory from a non-utf8 file system.
 
1489
 
 
1490
        top, and the abspath element in the output are unicode, all other paths
 
1491
        are utf8. Local disk IO is done via unicode calls to listdir etc.
 
1492
 
 
1493
        This is currently the fallback code path when the filesystem encoding is
 
1494
        not UTF-8. It may be better to implement an alternative so that we can
 
1495
        safely handle paths that are not properly decodable in the current
 
1496
        encoding.
 
1497
 
 
1498
        See DirReader.read_dir for details.
 
1499
        """
 
1500
        _utf8_encode = self._utf8_encode
 
1501
        _lstat = os.lstat
 
1502
        _listdir = os.listdir
 
1503
        _kind_from_mode = file_kind_from_stat_mode
 
1504
 
 
1505
        if prefix:
 
1506
            relprefix = prefix + '/'
1228
1507
        else:
1229
1508
            relprefix = ''
1230
1509
        top_slash = top + u'/'
1232
1511
        dirblock = []
1233
1512
        append = dirblock.append
1234
1513
        for name in sorted(_listdir(top)):
1235
 
            name_utf8 = _utf8_encode(name)[0]
 
1514
            try:
 
1515
                name_utf8 = _utf8_encode(name)[0]
 
1516
            except UnicodeDecodeError:
 
1517
                raise errors.BadFilenameEncoding(
 
1518
                    _utf8_encode(relprefix)[0] + name, _fs_enc)
1236
1519
            abspath = top_slash + name
1237
1520
            statvalue = _lstat(abspath)
1238
 
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1521
            kind = _kind_from_mode(statvalue.st_mode)
1239
1522
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1240
 
        yield (relroot, top), dirblock
1241
 
 
1242
 
        # push the user specified dirs from dirblock
1243
 
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1523
        return dirblock
1244
1524
 
1245
1525
 
1246
1526
def copy_tree(from_path, to_path, handlers={}):
1247
1527
    """Copy all of the entries in from_path into to_path.
1248
1528
 
1249
 
    :param from_path: The base directory to copy. 
 
1529
    :param from_path: The base directory to copy.
1250
1530
    :param to_path: The target directory. If it does not exist, it will
1251
1531
        be created.
1252
1532
    :param handlers: A dictionary of functions, which takes a source and
1321
1601
        return _cached_user_encoding
1322
1602
 
1323
1603
    if sys.platform == 'darwin':
1324
 
        # work around egregious python 2.4 bug
 
1604
        # python locale.getpreferredencoding() always return
 
1605
        # 'mac-roman' on darwin. That's a lie.
1325
1606
        sys.platform = 'posix'
1326
1607
        try:
 
1608
            if os.environ.get('LANG', None) is None:
 
1609
                # If LANG is not set, we end up with 'ascii', which is bad
 
1610
                # ('mac-roman' is more than ascii), so we set a default which
 
1611
                # will give us UTF-8 (which appears to work in all cases on
 
1612
                # OSX). Users are still free to override LANG of course, as
 
1613
                # long as it give us something meaningful. This work-around
 
1614
                # *may* not be needed with python 3k and/or OSX 10.5, but will
 
1615
                # work with them too -- vila 20080908
 
1616
                os.environ['LANG'] = 'en_US.UTF-8'
1327
1617
            import locale
1328
1618
        finally:
1329
1619
            sys.platform = 'darwin'
1344
1634
    # Windows returns 'cp0' to indicate there is no code page. So we'll just
1345
1635
    # treat that as ASCII, and not support printing unicode characters to the
1346
1636
    # console.
1347
 
    if user_encoding in (None, 'cp0'):
 
1637
    #
 
1638
    # For python scripts run under vim, we get '', so also treat that as ASCII
 
1639
    if user_encoding in (None, 'cp0', ''):
1348
1640
        user_encoding = 'ascii'
1349
1641
    else:
1350
1642
        # check encoding
1364
1656
    return user_encoding
1365
1657
 
1366
1658
 
 
1659
def get_host_name():
 
1660
    """Return the current unicode host name.
 
1661
 
 
1662
    This is meant to be used in place of socket.gethostname() because that
 
1663
    behaves inconsistently on different platforms.
 
1664
    """
 
1665
    if sys.platform == "win32":
 
1666
        import win32utils
 
1667
        return win32utils.get_host_name()
 
1668
    else:
 
1669
        import socket
 
1670
        return socket.gethostname().decode(get_user_encoding())
 
1671
 
 
1672
 
1367
1673
def recv_all(socket, bytes):
1368
1674
    """Receive an exact number of bytes.
1369
1675
 
1376
1682
    """
1377
1683
    b = ''
1378
1684
    while len(b) < bytes:
1379
 
        new = socket.recv(bytes - len(b))
 
1685
        new = until_no_eintr(socket.recv, bytes - len(b))
1380
1686
        if new == '':
1381
1687
            break # eof
1382
1688
        b += new
1383
1689
    return b
1384
1690
 
1385
1691
 
1386
 
def send_all(socket, bytes):
 
1692
def send_all(socket, bytes, report_activity=None):
1387
1693
    """Send all bytes on a socket.
1388
1694
 
1389
1695
    Regular socket.sendall() can give socket error 10053 on Windows.  This
1390
1696
    implementation sends no more than 64k at a time, which avoids this problem.
 
1697
 
 
1698
    :param report_activity: Call this as bytes are read, see
 
1699
        Transport._report_activity
1391
1700
    """
1392
1701
    chunk_size = 2**16
1393
1702
    for pos in xrange(0, len(bytes), chunk_size):
1394
 
        socket.sendall(bytes[pos:pos+chunk_size])
 
1703
        block = bytes[pos:pos+chunk_size]
 
1704
        if report_activity is not None:
 
1705
            report_activity(len(block), 'write')
 
1706
        until_no_eintr(socket.sendall, block)
1395
1707
 
1396
1708
 
1397
1709
def dereference_path(path):
1440
1752
        base = abspath(pathjoin(base, '..', '..'))
1441
1753
    filename = pathjoin(base, resource_relpath)
1442
1754
    return open(filename, 'rU').read()
 
1755
 
 
1756
 
 
1757
def file_kind_from_stat_mode_thunk(mode):
 
1758
    global file_kind_from_stat_mode
 
1759
    if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
 
1760
        try:
 
1761
            from bzrlib._readdir_pyx import UTF8DirReader
 
1762
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
 
1763
        except ImportError:
 
1764
            from bzrlib._readdir_py import (
 
1765
                _kind_from_mode as file_kind_from_stat_mode
 
1766
                )
 
1767
    return file_kind_from_stat_mode(mode)
 
1768
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
 
1769
 
 
1770
 
 
1771
def file_kind(f, _lstat=os.lstat):
 
1772
    try:
 
1773
        return file_kind_from_stat_mode(_lstat(f).st_mode)
 
1774
    except OSError, e:
 
1775
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
 
1776
            raise errors.NoSuchFile(f)
 
1777
        raise
 
1778
 
 
1779
 
 
1780
def until_no_eintr(f, *a, **kw):
 
1781
    """Run f(*a, **kw), retrying if an EINTR error occurs."""
 
1782
    # Borrowed from Twisted's twisted.python.util.untilConcludes function.
 
1783
    while True:
 
1784
        try:
 
1785
            return f(*a, **kw)
 
1786
        except (IOError, OSError), e:
 
1787
            if e.errno == errno.EINTR:
 
1788
                continue
 
1789
            raise
 
1790
 
 
1791
def re_compile_checked(re_string, flags=0, where=""):
 
1792
    """Return a compiled re, or raise a sensible error.
 
1793
 
 
1794
    This should only be used when compiling user-supplied REs.
 
1795
 
 
1796
    :param re_string: Text form of regular expression.
 
1797
    :param flags: eg re.IGNORECASE
 
1798
    :param where: Message explaining to the user the context where
 
1799
        it occurred, eg 'log search filter'.
 
1800
    """
 
1801
    # from https://bugs.launchpad.net/bzr/+bug/251352
 
1802
    try:
 
1803
        re_obj = re.compile(re_string, flags)
 
1804
        re_obj.search("")
 
1805
        return re_obj
 
1806
    except re.error, e:
 
1807
        if where:
 
1808
            where = ' in ' + where
 
1809
        # despite the name 'error' is a type
 
1810
        raise errors.BzrCommandError('Invalid regular expression%s: %r: %s'
 
1811
            % (where, re_string, e))
 
1812
 
 
1813
 
 
1814
if sys.platform == "win32":
 
1815
    import msvcrt
 
1816
    def getchar():
 
1817
        return msvcrt.getch()
 
1818
else:
 
1819
    import tty
 
1820
    import termios
 
1821
    def getchar():
 
1822
        fd = sys.stdin.fileno()
 
1823
        settings = termios.tcgetattr(fd)
 
1824
        try:
 
1825
            tty.setraw(fd)
 
1826
            ch = sys.stdin.read(1)
 
1827
        finally:
 
1828
            termios.tcsetattr(fd, termios.TCSADRAIN, settings)
 
1829
        return ch
 
1830
 
 
1831
 
 
1832
if sys.platform == 'linux2':
 
1833
    def _local_concurrency():
 
1834
        concurrency = None
 
1835
        prefix = 'processor'
 
1836
        for line in file('/proc/cpuinfo', 'rb'):
 
1837
            if line.startswith(prefix):
 
1838
                concurrency = int(line[line.find(':')+1:]) + 1
 
1839
        return concurrency
 
1840
elif sys.platform == 'darwin':
 
1841
    def _local_concurrency():
 
1842
        return subprocess.Popen(['sysctl', '-n', 'hw.availcpu'],
 
1843
                                stdout=subprocess.PIPE).communicate()[0]
 
1844
elif sys.platform[0:7] == 'freebsd':
 
1845
    def _local_concurrency():
 
1846
        return subprocess.Popen(['sysctl', '-n', 'hw.ncpu'],
 
1847
                                stdout=subprocess.PIPE).communicate()[0]
 
1848
elif sys.platform == 'sunos5':
 
1849
    def _local_concurrency():
 
1850
        return subprocess.Popen(['psrinfo', '-p',],
 
1851
                                stdout=subprocess.PIPE).communicate()[0]
 
1852
elif sys.platform == "win32":
 
1853
    def _local_concurrency():
 
1854
        # This appears to return the number of cores.
 
1855
        return os.environ.get('NUMBER_OF_PROCESSORS')
 
1856
else:
 
1857
    def _local_concurrency():
 
1858
        # Who knows ?
 
1859
        return None
 
1860
 
 
1861
 
 
1862
_cached_local_concurrency = None
 
1863
 
 
1864
def local_concurrency(use_cache=True):
 
1865
    """Return how many processes can be run concurrently.
 
1866
 
 
1867
    Rely on platform specific implementations and default to 1 (one) if
 
1868
    anything goes wrong.
 
1869
    """
 
1870
    global _cached_local_concurrency
 
1871
    if _cached_local_concurrency is not None and use_cache:
 
1872
        return _cached_local_concurrency
 
1873
 
 
1874
    try:
 
1875
        concurrency = _local_concurrency()
 
1876
    except (OSError, IOError):
 
1877
        concurrency = None
 
1878
    try:
 
1879
        concurrency = int(concurrency)
 
1880
    except (TypeError, ValueError):
 
1881
        concurrency = 1
 
1882
    if use_cache:
 
1883
        _cached_concurrency = concurrency
 
1884
    return concurrency