prasb commited on
Commit
af200b3
·
verified ·
1 Parent(s): 8b3b27b

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +4 -0
  2. my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_asyncio.cpython-38-x86_64-linux-gnu.so +3 -0
  3. my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_ctypes.cpython-38-x86_64-linux-gnu.so +3 -0
  4. my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_socket.cpython-38-x86_64-linux-gnu.so +3 -0
  5. my_container_sandbox/workspace/anaconda3/lib/python3.8/lib2to3/Grammar3.8.18.final.0.pickle +3 -0
  6. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/__pycache__/autopep8.cpython-38.pyc +3 -0
  7. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/__init__.py +24 -0
  8. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/_common.py +43 -0
  9. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/_version.py +4 -0
  10. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/easter.py +89 -0
  11. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/relativedelta.py +599 -0
  12. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/rrule.py +1737 -0
  13. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/tzwin.py +2 -0
  14. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/utils.py +71 -0
  15. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/__init__.py +65 -0
  16. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/_version.py +21 -0
  17. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/archive.py +68 -0
  18. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/asyn.py +933 -0
  19. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/caching.py +521 -0
  20. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/callbacks.py +238 -0
  21. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/compression.py +173 -0
  22. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/config.py +99 -0
  23. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/conftest.py +55 -0
  24. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/core.py +707 -0
  25. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/dircache.py +96 -0
  26. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/exceptions.py +21 -0
  27. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/fuse.py +324 -0
  28. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/generic.py +167 -0
  29. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/gui.py +408 -0
  30. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/mapping.py +232 -0
  31. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/parquet.py +551 -0
  32. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/registry.py +270 -0
  33. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/spec.py +1697 -0
  34. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/transaction.py +81 -0
  35. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/utils.py +621 -0
  36. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/LICENSE.txt +19 -0
  37. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/METADATA +107 -0
  38. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/RECORD +416 -0
  39. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/REQUESTED +0 -0
  40. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/WHEEL +5 -0
  41. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/top_level.txt +4 -0
  42. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/__init__.py +23 -0
  43. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/compose.py +62 -0
  44. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/errors.py +45 -0
  45. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/initialize.py +179 -0
  46. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/main.py +100 -0
  47. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/py.typed +0 -0
  48. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/types.py +82 -0
  49. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/utils.py +84 -0
  50. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/version.py +80 -0
.gitattributes CHANGED
@@ -255,3 +255,7 @@ my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_ssl.cpython-
255
  my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_testbuffer.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
256
  my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_sqlite3.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
257
  my_container_sandbox/workspace/anaconda3/lib/python3.8/tkinter/__pycache__/__init__.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
255
  my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_testbuffer.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
256
  my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_sqlite3.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
257
  my_container_sandbox/workspace/anaconda3/lib/python3.8/tkinter/__pycache__/__init__.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
258
+ my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_socket.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
259
+ my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_ctypes.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
260
+ my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/__pycache__/autopep8.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
261
+ my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_asyncio.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_asyncio.cpython-38-x86_64-linux-gnu.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7805a83a6295ab6cccb70abde69a7ac28d986b943f490387cb7948ff8033bb36
3
+ size 225344
my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_ctypes.cpython-38-x86_64-linux-gnu.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fbef76d31c5ef234af2c4686fc45de37fc51bb4ade60c77a893179db581ad297
3
+ size 513728
my_container_sandbox/workspace/anaconda3/lib/python3.8/lib-dynload/_socket.cpython-38-x86_64-linux-gnu.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5f5cd0ce9e6b42fc744a208bde0d6c23947cae12cd94493072414d85cd38a86d
3
+ size 283768
my_container_sandbox/workspace/anaconda3/lib/python3.8/lib2to3/Grammar3.8.18.final.0.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3131a0354ebd272fdb9f419a5045a4e814b8d28b4482f7ba2874eb6a1d4fc228
3
+ size 15309
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/__pycache__/autopep8.cpython-38.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ae195c0994a3df4625694e123f01da6ad6437932d70ddf7659a391631ae1e611
3
+ size 100765
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import sys
3
+
4
+ try:
5
+ from ._version import version as __version__
6
+ except ImportError:
7
+ __version__ = 'unknown'
8
+
9
+ __all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz',
10
+ 'utils', 'zoneinfo']
11
+
12
+ def __getattr__(name):
13
+ import importlib
14
+
15
+ if name in __all__:
16
+ return importlib.import_module("." + name, __name__)
17
+ raise AttributeError(
18
+ "module {!r} has not attribute {!r}".format(__name__, name)
19
+ )
20
+
21
+
22
+ def __dir__():
23
+ # __dir__ should include all the lazy-importable modules as well.
24
+ return [x for x in globals() if x not in sys.modules] + __all__
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/_common.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Common code used in multiple modules.
3
+ """
4
+
5
+
6
+ class weekday(object):
7
+ __slots__ = ["weekday", "n"]
8
+
9
+ def __init__(self, weekday, n=None):
10
+ self.weekday = weekday
11
+ self.n = n
12
+
13
+ def __call__(self, n):
14
+ if n == self.n:
15
+ return self
16
+ else:
17
+ return self.__class__(self.weekday, n)
18
+
19
+ def __eq__(self, other):
20
+ try:
21
+ if self.weekday != other.weekday or self.n != other.n:
22
+ return False
23
+ except AttributeError:
24
+ return False
25
+ return True
26
+
27
+ def __hash__(self):
28
+ return hash((
29
+ self.weekday,
30
+ self.n,
31
+ ))
32
+
33
+ def __ne__(self, other):
34
+ return not (self == other)
35
+
36
+ def __repr__(self):
37
+ s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday]
38
+ if not self.n:
39
+ return s
40
+ else:
41
+ return "%s(%+d)" % (s, self.n)
42
+
43
+ # vim:ts=4:sw=4:et
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/_version.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ __version__ = version = '2.9.0.post0'
4
+ __version_tuple__ = version_tuple = (2, 9, 0)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/easter.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module offers a generic Easter computing method for any given year, using
4
+ Western, Orthodox or Julian algorithms.
5
+ """
6
+
7
+ import datetime
8
+
9
+ __all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
10
+
11
+ EASTER_JULIAN = 1
12
+ EASTER_ORTHODOX = 2
13
+ EASTER_WESTERN = 3
14
+
15
+
16
+ def easter(year, method=EASTER_WESTERN):
17
+ """
18
+ This method was ported from the work done by GM Arts,
19
+ on top of the algorithm by Claus Tondering, which was
20
+ based in part on the algorithm of Ouding (1940), as
21
+ quoted in "Explanatory Supplement to the Astronomical
22
+ Almanac", P. Kenneth Seidelmann, editor.
23
+
24
+ This algorithm implements three different Easter
25
+ calculation methods:
26
+
27
+ 1. Original calculation in Julian calendar, valid in
28
+ dates after 326 AD
29
+ 2. Original method, with date converted to Gregorian
30
+ calendar, valid in years 1583 to 4099
31
+ 3. Revised method, in Gregorian calendar, valid in
32
+ years 1583 to 4099 as well
33
+
34
+ These methods are represented by the constants:
35
+
36
+ * ``EASTER_JULIAN = 1``
37
+ * ``EASTER_ORTHODOX = 2``
38
+ * ``EASTER_WESTERN = 3``
39
+
40
+ The default method is method 3.
41
+
42
+ More about the algorithm may be found at:
43
+
44
+ `GM Arts: Easter Algorithms <http://www.gmarts.org/index.php?go=415>`_
45
+
46
+ and
47
+
48
+ `The Calendar FAQ: Easter <https://www.tondering.dk/claus/cal/easter.php>`_
49
+
50
+ """
51
+
52
+ if not (1 <= method <= 3):
53
+ raise ValueError("invalid method")
54
+
55
+ # g - Golden year - 1
56
+ # c - Century
57
+ # h - (23 - Epact) mod 30
58
+ # i - Number of days from March 21 to Paschal Full Moon
59
+ # j - Weekday for PFM (0=Sunday, etc)
60
+ # p - Number of days from March 21 to Sunday on or before PFM
61
+ # (-6 to 28 methods 1 & 3, to 56 for method 2)
62
+ # e - Extra days to add for method 2 (converting Julian
63
+ # date to Gregorian date)
64
+
65
+ y = year
66
+ g = y % 19
67
+ e = 0
68
+ if method < 3:
69
+ # Old method
70
+ i = (19*g + 15) % 30
71
+ j = (y + y//4 + i) % 7
72
+ if method == 2:
73
+ # Extra dates to convert Julian to Gregorian date
74
+ e = 10
75
+ if y > 1600:
76
+ e = e + y//100 - 16 - (y//100 - 16)//4
77
+ else:
78
+ # New method
79
+ c = y//100
80
+ h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30
81
+ i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11))
82
+ j = (y + y//4 + i + 2 - c + c//4) % 7
83
+
84
+ # p can be from -6 to 56 corresponding to dates 22 March to 23 May
85
+ # (later dates apply to method 2, although 23 May never actually occurs)
86
+ p = i - j + e
87
+ d = 1 + (p + 27 + (p + 6)//40) % 31
88
+ m = 3 + (p + 26)//30
89
+ return datetime.date(int(y), int(m), int(d))
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/relativedelta.py ADDED
@@ -0,0 +1,599 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import datetime
3
+ import calendar
4
+
5
+ import operator
6
+ from math import copysign
7
+
8
+ from six import integer_types
9
+ from warnings import warn
10
+
11
+ from ._common import weekday
12
+
13
+ MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7))
14
+
15
+ __all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
16
+
17
+
18
+ class relativedelta(object):
19
+ """
20
+ The relativedelta type is designed to be applied to an existing datetime and
21
+ can replace specific components of that datetime, or represents an interval
22
+ of time.
23
+
24
+ It is based on the specification of the excellent work done by M.-A. Lemburg
25
+ in his
26
+ `mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension.
27
+ However, notice that this type does *NOT* implement the same algorithm as
28
+ his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.
29
+
30
+ There are two different ways to build a relativedelta instance. The
31
+ first one is passing it two date/datetime classes::
32
+
33
+ relativedelta(datetime1, datetime2)
34
+
35
+ The second one is passing it any number of the following keyword arguments::
36
+
37
+ relativedelta(arg1=x,arg2=y,arg3=z...)
38
+
39
+ year, month, day, hour, minute, second, microsecond:
40
+ Absolute information (argument is singular); adding or subtracting a
41
+ relativedelta with absolute information does not perform an arithmetic
42
+ operation, but rather REPLACES the corresponding value in the
43
+ original datetime with the value(s) in relativedelta.
44
+
45
+ years, months, weeks, days, hours, minutes, seconds, microseconds:
46
+ Relative information, may be negative (argument is plural); adding
47
+ or subtracting a relativedelta with relative information performs
48
+ the corresponding arithmetic operation on the original datetime value
49
+ with the information in the relativedelta.
50
+
51
+ weekday:
52
+ One of the weekday instances (MO, TU, etc) available in the
53
+ relativedelta module. These instances may receive a parameter N,
54
+ specifying the Nth weekday, which could be positive or negative
55
+ (like MO(+1) or MO(-2)). Not specifying it is the same as specifying
56
+ +1. You can also use an integer, where 0=MO. This argument is always
57
+ relative e.g. if the calculated date is already Monday, using MO(1)
58
+ or MO(-1) won't change the day. To effectively make it absolute, use
59
+ it in combination with the day argument (e.g. day=1, MO(1) for first
60
+ Monday of the month).
61
+
62
+ leapdays:
63
+ Will add given days to the date found, if year is a leap
64
+ year, and the date found is post 28 of february.
65
+
66
+ yearday, nlyearday:
67
+ Set the yearday or the non-leap year day (jump leap days).
68
+ These are converted to day/month/leapdays information.
69
+
70
+ There are relative and absolute forms of the keyword
71
+ arguments. The plural is relative, and the singular is
72
+ absolute. For each argument in the order below, the absolute form
73
+ is applied first (by setting each attribute to that value) and
74
+ then the relative form (by adding the value to the attribute).
75
+
76
+ The order of attributes considered when this relativedelta is
77
+ added to a datetime is:
78
+
79
+ 1. Year
80
+ 2. Month
81
+ 3. Day
82
+ 4. Hours
83
+ 5. Minutes
84
+ 6. Seconds
85
+ 7. Microseconds
86
+
87
+ Finally, weekday is applied, using the rule described above.
88
+
89
+ For example
90
+
91
+ >>> from datetime import datetime
92
+ >>> from dateutil.relativedelta import relativedelta, MO
93
+ >>> dt = datetime(2018, 4, 9, 13, 37, 0)
94
+ >>> delta = relativedelta(hours=25, day=1, weekday=MO(1))
95
+ >>> dt + delta
96
+ datetime.datetime(2018, 4, 2, 14, 37)
97
+
98
+ First, the day is set to 1 (the first of the month), then 25 hours
99
+ are added, to get to the 2nd day and 14th hour, finally the
100
+ weekday is applied, but since the 2nd is already a Monday there is
101
+ no effect.
102
+
103
+ """
104
+
105
+ def __init__(self, dt1=None, dt2=None,
106
+ years=0, months=0, days=0, leapdays=0, weeks=0,
107
+ hours=0, minutes=0, seconds=0, microseconds=0,
108
+ year=None, month=None, day=None, weekday=None,
109
+ yearday=None, nlyearday=None,
110
+ hour=None, minute=None, second=None, microsecond=None):
111
+
112
+ if dt1 and dt2:
113
+ # datetime is a subclass of date. So both must be date
114
+ if not (isinstance(dt1, datetime.date) and
115
+ isinstance(dt2, datetime.date)):
116
+ raise TypeError("relativedelta only diffs datetime/date")
117
+
118
+ # We allow two dates, or two datetimes, so we coerce them to be
119
+ # of the same type
120
+ if (isinstance(dt1, datetime.datetime) !=
121
+ isinstance(dt2, datetime.datetime)):
122
+ if not isinstance(dt1, datetime.datetime):
123
+ dt1 = datetime.datetime.fromordinal(dt1.toordinal())
124
+ elif not isinstance(dt2, datetime.datetime):
125
+ dt2 = datetime.datetime.fromordinal(dt2.toordinal())
126
+
127
+ self.years = 0
128
+ self.months = 0
129
+ self.days = 0
130
+ self.leapdays = 0
131
+ self.hours = 0
132
+ self.minutes = 0
133
+ self.seconds = 0
134
+ self.microseconds = 0
135
+ self.year = None
136
+ self.month = None
137
+ self.day = None
138
+ self.weekday = None
139
+ self.hour = None
140
+ self.minute = None
141
+ self.second = None
142
+ self.microsecond = None
143
+ self._has_time = 0
144
+
145
+ # Get year / month delta between the two
146
+ months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month)
147
+ self._set_months(months)
148
+
149
+ # Remove the year/month delta so the timedelta is just well-defined
150
+ # time units (seconds, days and microseconds)
151
+ dtm = self.__radd__(dt2)
152
+
153
+ # If we've overshot our target, make an adjustment
154
+ if dt1 < dt2:
155
+ compare = operator.gt
156
+ increment = 1
157
+ else:
158
+ compare = operator.lt
159
+ increment = -1
160
+
161
+ while compare(dt1, dtm):
162
+ months += increment
163
+ self._set_months(months)
164
+ dtm = self.__radd__(dt2)
165
+
166
+ # Get the timedelta between the "months-adjusted" date and dt1
167
+ delta = dt1 - dtm
168
+ self.seconds = delta.seconds + delta.days * 86400
169
+ self.microseconds = delta.microseconds
170
+ else:
171
+ # Check for non-integer values in integer-only quantities
172
+ if any(x is not None and x != int(x) for x in (years, months)):
173
+ raise ValueError("Non-integer years and months are "
174
+ "ambiguous and not currently supported.")
175
+
176
+ # Relative information
177
+ self.years = int(years)
178
+ self.months = int(months)
179
+ self.days = days + weeks * 7
180
+ self.leapdays = leapdays
181
+ self.hours = hours
182
+ self.minutes = minutes
183
+ self.seconds = seconds
184
+ self.microseconds = microseconds
185
+
186
+ # Absolute information
187
+ self.year = year
188
+ self.month = month
189
+ self.day = day
190
+ self.hour = hour
191
+ self.minute = minute
192
+ self.second = second
193
+ self.microsecond = microsecond
194
+
195
+ if any(x is not None and int(x) != x
196
+ for x in (year, month, day, hour,
197
+ minute, second, microsecond)):
198
+ # For now we'll deprecate floats - later it'll be an error.
199
+ warn("Non-integer value passed as absolute information. " +
200
+ "This is not a well-defined condition and will raise " +
201
+ "errors in future versions.", DeprecationWarning)
202
+
203
+ if isinstance(weekday, integer_types):
204
+ self.weekday = weekdays[weekday]
205
+ else:
206
+ self.weekday = weekday
207
+
208
+ yday = 0
209
+ if nlyearday:
210
+ yday = nlyearday
211
+ elif yearday:
212
+ yday = yearday
213
+ if yearday > 59:
214
+ self.leapdays = -1
215
+ if yday:
216
+ ydayidx = [31, 59, 90, 120, 151, 181, 212,
217
+ 243, 273, 304, 334, 366]
218
+ for idx, ydays in enumerate(ydayidx):
219
+ if yday <= ydays:
220
+ self.month = idx+1
221
+ if idx == 0:
222
+ self.day = yday
223
+ else:
224
+ self.day = yday-ydayidx[idx-1]
225
+ break
226
+ else:
227
+ raise ValueError("invalid year day (%d)" % yday)
228
+
229
+ self._fix()
230
+
231
+ def _fix(self):
232
+ if abs(self.microseconds) > 999999:
233
+ s = _sign(self.microseconds)
234
+ div, mod = divmod(self.microseconds * s, 1000000)
235
+ self.microseconds = mod * s
236
+ self.seconds += div * s
237
+ if abs(self.seconds) > 59:
238
+ s = _sign(self.seconds)
239
+ div, mod = divmod(self.seconds * s, 60)
240
+ self.seconds = mod * s
241
+ self.minutes += div * s
242
+ if abs(self.minutes) > 59:
243
+ s = _sign(self.minutes)
244
+ div, mod = divmod(self.minutes * s, 60)
245
+ self.minutes = mod * s
246
+ self.hours += div * s
247
+ if abs(self.hours) > 23:
248
+ s = _sign(self.hours)
249
+ div, mod = divmod(self.hours * s, 24)
250
+ self.hours = mod * s
251
+ self.days += div * s
252
+ if abs(self.months) > 11:
253
+ s = _sign(self.months)
254
+ div, mod = divmod(self.months * s, 12)
255
+ self.months = mod * s
256
+ self.years += div * s
257
+ if (self.hours or self.minutes or self.seconds or self.microseconds
258
+ or self.hour is not None or self.minute is not None or
259
+ self.second is not None or self.microsecond is not None):
260
+ self._has_time = 1
261
+ else:
262
+ self._has_time = 0
263
+
264
+ @property
265
+ def weeks(self):
266
+ return int(self.days / 7.0)
267
+
268
+ @weeks.setter
269
+ def weeks(self, value):
270
+ self.days = self.days - (self.weeks * 7) + value * 7
271
+
272
+ def _set_months(self, months):
273
+ self.months = months
274
+ if abs(self.months) > 11:
275
+ s = _sign(self.months)
276
+ div, mod = divmod(self.months * s, 12)
277
+ self.months = mod * s
278
+ self.years = div * s
279
+ else:
280
+ self.years = 0
281
+
282
+ def normalized(self):
283
+ """
284
+ Return a version of this object represented entirely using integer
285
+ values for the relative attributes.
286
+
287
+ >>> relativedelta(days=1.5, hours=2).normalized()
288
+ relativedelta(days=+1, hours=+14)
289
+
290
+ :return:
291
+ Returns a :class:`dateutil.relativedelta.relativedelta` object.
292
+ """
293
+ # Cascade remainders down (rounding each to roughly nearest microsecond)
294
+ days = int(self.days)
295
+
296
+ hours_f = round(self.hours + 24 * (self.days - days), 11)
297
+ hours = int(hours_f)
298
+
299
+ minutes_f = round(self.minutes + 60 * (hours_f - hours), 10)
300
+ minutes = int(minutes_f)
301
+
302
+ seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8)
303
+ seconds = int(seconds_f)
304
+
305
+ microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds))
306
+
307
+ # Constructor carries overflow back up with call to _fix()
308
+ return self.__class__(years=self.years, months=self.months,
309
+ days=days, hours=hours, minutes=minutes,
310
+ seconds=seconds, microseconds=microseconds,
311
+ leapdays=self.leapdays, year=self.year,
312
+ month=self.month, day=self.day,
313
+ weekday=self.weekday, hour=self.hour,
314
+ minute=self.minute, second=self.second,
315
+ microsecond=self.microsecond)
316
+
317
+ def __add__(self, other):
318
+ if isinstance(other, relativedelta):
319
+ return self.__class__(years=other.years + self.years,
320
+ months=other.months + self.months,
321
+ days=other.days + self.days,
322
+ hours=other.hours + self.hours,
323
+ minutes=other.minutes + self.minutes,
324
+ seconds=other.seconds + self.seconds,
325
+ microseconds=(other.microseconds +
326
+ self.microseconds),
327
+ leapdays=other.leapdays or self.leapdays,
328
+ year=(other.year if other.year is not None
329
+ else self.year),
330
+ month=(other.month if other.month is not None
331
+ else self.month),
332
+ day=(other.day if other.day is not None
333
+ else self.day),
334
+ weekday=(other.weekday if other.weekday is not None
335
+ else self.weekday),
336
+ hour=(other.hour if other.hour is not None
337
+ else self.hour),
338
+ minute=(other.minute if other.minute is not None
339
+ else self.minute),
340
+ second=(other.second if other.second is not None
341
+ else self.second),
342
+ microsecond=(other.microsecond if other.microsecond
343
+ is not None else
344
+ self.microsecond))
345
+ if isinstance(other, datetime.timedelta):
346
+ return self.__class__(years=self.years,
347
+ months=self.months,
348
+ days=self.days + other.days,
349
+ hours=self.hours,
350
+ minutes=self.minutes,
351
+ seconds=self.seconds + other.seconds,
352
+ microseconds=self.microseconds + other.microseconds,
353
+ leapdays=self.leapdays,
354
+ year=self.year,
355
+ month=self.month,
356
+ day=self.day,
357
+ weekday=self.weekday,
358
+ hour=self.hour,
359
+ minute=self.minute,
360
+ second=self.second,
361
+ microsecond=self.microsecond)
362
+ if not isinstance(other, datetime.date):
363
+ return NotImplemented
364
+ elif self._has_time and not isinstance(other, datetime.datetime):
365
+ other = datetime.datetime.fromordinal(other.toordinal())
366
+ year = (self.year or other.year)+self.years
367
+ month = self.month or other.month
368
+ if self.months:
369
+ assert 1 <= abs(self.months) <= 12
370
+ month += self.months
371
+ if month > 12:
372
+ year += 1
373
+ month -= 12
374
+ elif month < 1:
375
+ year -= 1
376
+ month += 12
377
+ day = min(calendar.monthrange(year, month)[1],
378
+ self.day or other.day)
379
+ repl = {"year": year, "month": month, "day": day}
380
+ for attr in ["hour", "minute", "second", "microsecond"]:
381
+ value = getattr(self, attr)
382
+ if value is not None:
383
+ repl[attr] = value
384
+ days = self.days
385
+ if self.leapdays and month > 2 and calendar.isleap(year):
386
+ days += self.leapdays
387
+ ret = (other.replace(**repl)
388
+ + datetime.timedelta(days=days,
389
+ hours=self.hours,
390
+ minutes=self.minutes,
391
+ seconds=self.seconds,
392
+ microseconds=self.microseconds))
393
+ if self.weekday:
394
+ weekday, nth = self.weekday.weekday, self.weekday.n or 1
395
+ jumpdays = (abs(nth) - 1) * 7
396
+ if nth > 0:
397
+ jumpdays += (7 - ret.weekday() + weekday) % 7
398
+ else:
399
+ jumpdays += (ret.weekday() - weekday) % 7
400
+ jumpdays *= -1
401
+ ret += datetime.timedelta(days=jumpdays)
402
+ return ret
403
+
404
+ def __radd__(self, other):
405
+ return self.__add__(other)
406
+
407
+ def __rsub__(self, other):
408
+ return self.__neg__().__radd__(other)
409
+
410
+ def __sub__(self, other):
411
+ if not isinstance(other, relativedelta):
412
+ return NotImplemented # In case the other object defines __rsub__
413
+ return self.__class__(years=self.years - other.years,
414
+ months=self.months - other.months,
415
+ days=self.days - other.days,
416
+ hours=self.hours - other.hours,
417
+ minutes=self.minutes - other.minutes,
418
+ seconds=self.seconds - other.seconds,
419
+ microseconds=self.microseconds - other.microseconds,
420
+ leapdays=self.leapdays or other.leapdays,
421
+ year=(self.year if self.year is not None
422
+ else other.year),
423
+ month=(self.month if self.month is not None else
424
+ other.month),
425
+ day=(self.day if self.day is not None else
426
+ other.day),
427
+ weekday=(self.weekday if self.weekday is not None else
428
+ other.weekday),
429
+ hour=(self.hour if self.hour is not None else
430
+ other.hour),
431
+ minute=(self.minute if self.minute is not None else
432
+ other.minute),
433
+ second=(self.second if self.second is not None else
434
+ other.second),
435
+ microsecond=(self.microsecond if self.microsecond
436
+ is not None else
437
+ other.microsecond))
438
+
439
+ def __abs__(self):
440
+ return self.__class__(years=abs(self.years),
441
+ months=abs(self.months),
442
+ days=abs(self.days),
443
+ hours=abs(self.hours),
444
+ minutes=abs(self.minutes),
445
+ seconds=abs(self.seconds),
446
+ microseconds=abs(self.microseconds),
447
+ leapdays=self.leapdays,
448
+ year=self.year,
449
+ month=self.month,
450
+ day=self.day,
451
+ weekday=self.weekday,
452
+ hour=self.hour,
453
+ minute=self.minute,
454
+ second=self.second,
455
+ microsecond=self.microsecond)
456
+
457
+ def __neg__(self):
458
+ return self.__class__(years=-self.years,
459
+ months=-self.months,
460
+ days=-self.days,
461
+ hours=-self.hours,
462
+ minutes=-self.minutes,
463
+ seconds=-self.seconds,
464
+ microseconds=-self.microseconds,
465
+ leapdays=self.leapdays,
466
+ year=self.year,
467
+ month=self.month,
468
+ day=self.day,
469
+ weekday=self.weekday,
470
+ hour=self.hour,
471
+ minute=self.minute,
472
+ second=self.second,
473
+ microsecond=self.microsecond)
474
+
475
+ def __bool__(self):
476
+ return not (not self.years and
477
+ not self.months and
478
+ not self.days and
479
+ not self.hours and
480
+ not self.minutes and
481
+ not self.seconds and
482
+ not self.microseconds and
483
+ not self.leapdays and
484
+ self.year is None and
485
+ self.month is None and
486
+ self.day is None and
487
+ self.weekday is None and
488
+ self.hour is None and
489
+ self.minute is None and
490
+ self.second is None and
491
+ self.microsecond is None)
492
+ # Compatibility with Python 2.x
493
+ __nonzero__ = __bool__
494
+
495
+ def __mul__(self, other):
496
+ try:
497
+ f = float(other)
498
+ except TypeError:
499
+ return NotImplemented
500
+
501
+ return self.__class__(years=int(self.years * f),
502
+ months=int(self.months * f),
503
+ days=int(self.days * f),
504
+ hours=int(self.hours * f),
505
+ minutes=int(self.minutes * f),
506
+ seconds=int(self.seconds * f),
507
+ microseconds=int(self.microseconds * f),
508
+ leapdays=self.leapdays,
509
+ year=self.year,
510
+ month=self.month,
511
+ day=self.day,
512
+ weekday=self.weekday,
513
+ hour=self.hour,
514
+ minute=self.minute,
515
+ second=self.second,
516
+ microsecond=self.microsecond)
517
+
518
+ __rmul__ = __mul__
519
+
520
+ def __eq__(self, other):
521
+ if not isinstance(other, relativedelta):
522
+ return NotImplemented
523
+ if self.weekday or other.weekday:
524
+ if not self.weekday or not other.weekday:
525
+ return False
526
+ if self.weekday.weekday != other.weekday.weekday:
527
+ return False
528
+ n1, n2 = self.weekday.n, other.weekday.n
529
+ if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)):
530
+ return False
531
+ return (self.years == other.years and
532
+ self.months == other.months and
533
+ self.days == other.days and
534
+ self.hours == other.hours and
535
+ self.minutes == other.minutes and
536
+ self.seconds == other.seconds and
537
+ self.microseconds == other.microseconds and
538
+ self.leapdays == other.leapdays and
539
+ self.year == other.year and
540
+ self.month == other.month and
541
+ self.day == other.day and
542
+ self.hour == other.hour and
543
+ self.minute == other.minute and
544
+ self.second == other.second and
545
+ self.microsecond == other.microsecond)
546
+
547
+ def __hash__(self):
548
+ return hash((
549
+ self.weekday,
550
+ self.years,
551
+ self.months,
552
+ self.days,
553
+ self.hours,
554
+ self.minutes,
555
+ self.seconds,
556
+ self.microseconds,
557
+ self.leapdays,
558
+ self.year,
559
+ self.month,
560
+ self.day,
561
+ self.hour,
562
+ self.minute,
563
+ self.second,
564
+ self.microsecond,
565
+ ))
566
+
567
+ def __ne__(self, other):
568
+ return not self.__eq__(other)
569
+
570
+ def __div__(self, other):
571
+ try:
572
+ reciprocal = 1 / float(other)
573
+ except TypeError:
574
+ return NotImplemented
575
+
576
+ return self.__mul__(reciprocal)
577
+
578
+ __truediv__ = __div__
579
+
580
+ def __repr__(self):
581
+ l = []
582
+ for attr in ["years", "months", "days", "leapdays",
583
+ "hours", "minutes", "seconds", "microseconds"]:
584
+ value = getattr(self, attr)
585
+ if value:
586
+ l.append("{attr}={value:+g}".format(attr=attr, value=value))
587
+ for attr in ["year", "month", "day", "weekday",
588
+ "hour", "minute", "second", "microsecond"]:
589
+ value = getattr(self, attr)
590
+ if value is not None:
591
+ l.append("{attr}={value}".format(attr=attr, value=repr(value)))
592
+ return "{classname}({attrs})".format(classname=self.__class__.__name__,
593
+ attrs=", ".join(l))
594
+
595
+
596
+ def _sign(x):
597
+ return int(copysign(1, x))
598
+
599
+ # vim:ts=4:sw=4:et
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/rrule.py ADDED
@@ -0,0 +1,1737 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ The rrule module offers a small, complete, and very fast, implementation of
4
+ the recurrence rules documented in the
5
+ `iCalendar RFC <https://tools.ietf.org/html/rfc5545>`_,
6
+ including support for caching of results.
7
+ """
8
+ import calendar
9
+ import datetime
10
+ import heapq
11
+ import itertools
12
+ import re
13
+ import sys
14
+ from functools import wraps
15
+ # For warning about deprecation of until and count
16
+ from warnings import warn
17
+
18
+ from six import advance_iterator, integer_types
19
+
20
+ from six.moves import _thread, range
21
+
22
+ from ._common import weekday as weekdaybase
23
+
24
+ try:
25
+ from math import gcd
26
+ except ImportError:
27
+ from fractions import gcd
28
+
29
+ __all__ = ["rrule", "rruleset", "rrulestr",
30
+ "YEARLY", "MONTHLY", "WEEKLY", "DAILY",
31
+ "HOURLY", "MINUTELY", "SECONDLY",
32
+ "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
33
+
34
+ # Every mask is 7 days longer to handle cross-year weekly periods.
35
+ M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 +
36
+ [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7)
37
+ M365MASK = list(M366MASK)
38
+ M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32))
39
+ MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7])
40
+ MDAY365MASK = list(MDAY366MASK)
41
+ M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0))
42
+ NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7])
43
+ NMDAY365MASK = list(NMDAY366MASK)
44
+ M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366)
45
+ M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
46
+ WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55
47
+ del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31]
48
+ MDAY365MASK = tuple(MDAY365MASK)
49
+ M365MASK = tuple(M365MASK)
50
+
51
+ FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY']
52
+
53
+ (YEARLY,
54
+ MONTHLY,
55
+ WEEKLY,
56
+ DAILY,
57
+ HOURLY,
58
+ MINUTELY,
59
+ SECONDLY) = list(range(7))
60
+
61
+ # Imported on demand.
62
+ easter = None
63
+ parser = None
64
+
65
+
66
+ class weekday(weekdaybase):
67
+ """
68
+ This version of weekday does not allow n = 0.
69
+ """
70
+ def __init__(self, wkday, n=None):
71
+ if n == 0:
72
+ raise ValueError("Can't create weekday with n==0")
73
+
74
+ super(weekday, self).__init__(wkday, n)
75
+
76
+
77
+ MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7))
78
+
79
+
80
+ def _invalidates_cache(f):
81
+ """
82
+ Decorator for rruleset methods which may invalidate the
83
+ cached length.
84
+ """
85
+ @wraps(f)
86
+ def inner_func(self, *args, **kwargs):
87
+ rv = f(self, *args, **kwargs)
88
+ self._invalidate_cache()
89
+ return rv
90
+
91
+ return inner_func
92
+
93
+
94
+ class rrulebase(object):
95
+ def __init__(self, cache=False):
96
+ if cache:
97
+ self._cache = []
98
+ self._cache_lock = _thread.allocate_lock()
99
+ self._invalidate_cache()
100
+ else:
101
+ self._cache = None
102
+ self._cache_complete = False
103
+ self._len = None
104
+
105
+ def __iter__(self):
106
+ if self._cache_complete:
107
+ return iter(self._cache)
108
+ elif self._cache is None:
109
+ return self._iter()
110
+ else:
111
+ return self._iter_cached()
112
+
113
+ def _invalidate_cache(self):
114
+ if self._cache is not None:
115
+ self._cache = []
116
+ self._cache_complete = False
117
+ self._cache_gen = self._iter()
118
+
119
+ if self._cache_lock.locked():
120
+ self._cache_lock.release()
121
+
122
+ self._len = None
123
+
124
+ def _iter_cached(self):
125
+ i = 0
126
+ gen = self._cache_gen
127
+ cache = self._cache
128
+ acquire = self._cache_lock.acquire
129
+ release = self._cache_lock.release
130
+ while gen:
131
+ if i == len(cache):
132
+ acquire()
133
+ if self._cache_complete:
134
+ break
135
+ try:
136
+ for j in range(10):
137
+ cache.append(advance_iterator(gen))
138
+ except StopIteration:
139
+ self._cache_gen = gen = None
140
+ self._cache_complete = True
141
+ break
142
+ release()
143
+ yield cache[i]
144
+ i += 1
145
+ while i < self._len:
146
+ yield cache[i]
147
+ i += 1
148
+
149
+ def __getitem__(self, item):
150
+ if self._cache_complete:
151
+ return self._cache[item]
152
+ elif isinstance(item, slice):
153
+ if item.step and item.step < 0:
154
+ return list(iter(self))[item]
155
+ else:
156
+ return list(itertools.islice(self,
157
+ item.start or 0,
158
+ item.stop or sys.maxsize,
159
+ item.step or 1))
160
+ elif item >= 0:
161
+ gen = iter(self)
162
+ try:
163
+ for i in range(item+1):
164
+ res = advance_iterator(gen)
165
+ except StopIteration:
166
+ raise IndexError
167
+ return res
168
+ else:
169
+ return list(iter(self))[item]
170
+
171
+ def __contains__(self, item):
172
+ if self._cache_complete:
173
+ return item in self._cache
174
+ else:
175
+ for i in self:
176
+ if i == item:
177
+ return True
178
+ elif i > item:
179
+ return False
180
+ return False
181
+
182
+ # __len__() introduces a large performance penalty.
183
+ def count(self):
184
+ """ Returns the number of recurrences in this set. It will have go
185
+ through the whole recurrence, if this hasn't been done before. """
186
+ if self._len is None:
187
+ for x in self:
188
+ pass
189
+ return self._len
190
+
191
+ def before(self, dt, inc=False):
192
+ """ Returns the last recurrence before the given datetime instance. The
193
+ inc keyword defines what happens if dt is an occurrence. With
194
+ inc=True, if dt itself is an occurrence, it will be returned. """
195
+ if self._cache_complete:
196
+ gen = self._cache
197
+ else:
198
+ gen = self
199
+ last = None
200
+ if inc:
201
+ for i in gen:
202
+ if i > dt:
203
+ break
204
+ last = i
205
+ else:
206
+ for i in gen:
207
+ if i >= dt:
208
+ break
209
+ last = i
210
+ return last
211
+
212
+ def after(self, dt, inc=False):
213
+ """ Returns the first recurrence after the given datetime instance. The
214
+ inc keyword defines what happens if dt is an occurrence. With
215
+ inc=True, if dt itself is an occurrence, it will be returned. """
216
+ if self._cache_complete:
217
+ gen = self._cache
218
+ else:
219
+ gen = self
220
+ if inc:
221
+ for i in gen:
222
+ if i >= dt:
223
+ return i
224
+ else:
225
+ for i in gen:
226
+ if i > dt:
227
+ return i
228
+ return None
229
+
230
+ def xafter(self, dt, count=None, inc=False):
231
+ """
232
+ Generator which yields up to `count` recurrences after the given
233
+ datetime instance, equivalent to `after`.
234
+
235
+ :param dt:
236
+ The datetime at which to start generating recurrences.
237
+
238
+ :param count:
239
+ The maximum number of recurrences to generate. If `None` (default),
240
+ dates are generated until the recurrence rule is exhausted.
241
+
242
+ :param inc:
243
+ If `dt` is an instance of the rule and `inc` is `True`, it is
244
+ included in the output.
245
+
246
+ :yields: Yields a sequence of `datetime` objects.
247
+ """
248
+
249
+ if self._cache_complete:
250
+ gen = self._cache
251
+ else:
252
+ gen = self
253
+
254
+ # Select the comparison function
255
+ if inc:
256
+ comp = lambda dc, dtc: dc >= dtc
257
+ else:
258
+ comp = lambda dc, dtc: dc > dtc
259
+
260
+ # Generate dates
261
+ n = 0
262
+ for d in gen:
263
+ if comp(d, dt):
264
+ if count is not None:
265
+ n += 1
266
+ if n > count:
267
+ break
268
+
269
+ yield d
270
+
271
+ def between(self, after, before, inc=False, count=1):
272
+ """ Returns all the occurrences of the rrule between after and before.
273
+ The inc keyword defines what happens if after and/or before are
274
+ themselves occurrences. With inc=True, they will be included in the
275
+ list, if they are found in the recurrence set. """
276
+ if self._cache_complete:
277
+ gen = self._cache
278
+ else:
279
+ gen = self
280
+ started = False
281
+ l = []
282
+ if inc:
283
+ for i in gen:
284
+ if i > before:
285
+ break
286
+ elif not started:
287
+ if i >= after:
288
+ started = True
289
+ l.append(i)
290
+ else:
291
+ l.append(i)
292
+ else:
293
+ for i in gen:
294
+ if i >= before:
295
+ break
296
+ elif not started:
297
+ if i > after:
298
+ started = True
299
+ l.append(i)
300
+ else:
301
+ l.append(i)
302
+ return l
303
+
304
+
305
+ class rrule(rrulebase):
306
+ """
307
+ That's the base of the rrule operation. It accepts all the keywords
308
+ defined in the RFC as its constructor parameters (except byday,
309
+ which was renamed to byweekday) and more. The constructor prototype is::
310
+
311
+ rrule(freq)
312
+
313
+ Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY,
314
+ or SECONDLY.
315
+
316
+ .. note::
317
+ Per RFC section 3.3.10, recurrence instances falling on invalid dates
318
+ and times are ignored rather than coerced:
319
+
320
+ Recurrence rules may generate recurrence instances with an invalid
321
+ date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM
322
+ on a day where the local time is moved forward by an hour at 1:00
323
+ AM). Such recurrence instances MUST be ignored and MUST NOT be
324
+ counted as part of the recurrence set.
325
+
326
+ This can lead to possibly surprising behavior when, for example, the
327
+ start date occurs at the end of the month:
328
+
329
+ >>> from dateutil.rrule import rrule, MONTHLY
330
+ >>> from datetime import datetime
331
+ >>> start_date = datetime(2014, 12, 31)
332
+ >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date))
333
+ ... # doctest: +NORMALIZE_WHITESPACE
334
+ [datetime.datetime(2014, 12, 31, 0, 0),
335
+ datetime.datetime(2015, 1, 31, 0, 0),
336
+ datetime.datetime(2015, 3, 31, 0, 0),
337
+ datetime.datetime(2015, 5, 31, 0, 0)]
338
+
339
+ Additionally, it supports the following keyword arguments:
340
+
341
+ :param dtstart:
342
+ The recurrence start. Besides being the base for the recurrence,
343
+ missing parameters in the final recurrence instances will also be
344
+ extracted from this date. If not given, datetime.now() will be used
345
+ instead.
346
+ :param interval:
347
+ The interval between each freq iteration. For example, when using
348
+ YEARLY, an interval of 2 means once every two years, but with HOURLY,
349
+ it means once every two hours. The default interval is 1.
350
+ :param wkst:
351
+ The week start day. Must be one of the MO, TU, WE constants, or an
352
+ integer, specifying the first day of the week. This will affect
353
+ recurrences based on weekly periods. The default week start is got
354
+ from calendar.firstweekday(), and may be modified by
355
+ calendar.setfirstweekday().
356
+ :param count:
357
+ If given, this determines how many occurrences will be generated.
358
+
359
+ .. note::
360
+ As of version 2.5.0, the use of the keyword ``until`` in conjunction
361
+ with ``count`` is deprecated, to make sure ``dateutil`` is fully
362
+ compliant with `RFC-5545 Sec. 3.3.10 <https://tools.ietf.org/
363
+ html/rfc5545#section-3.3.10>`_. Therefore, ``until`` and ``count``
364
+ **must not** occur in the same call to ``rrule``.
365
+ :param until:
366
+ If given, this must be a datetime instance specifying the upper-bound
367
+ limit of the recurrence. The last recurrence in the rule is the greatest
368
+ datetime that is less than or equal to the value specified in the
369
+ ``until`` parameter.
370
+
371
+ .. note::
372
+ As of version 2.5.0, the use of the keyword ``until`` in conjunction
373
+ with ``count`` is deprecated, to make sure ``dateutil`` is fully
374
+ compliant with `RFC-5545 Sec. 3.3.10 <https://tools.ietf.org/
375
+ html/rfc5545#section-3.3.10>`_. Therefore, ``until`` and ``count``
376
+ **must not** occur in the same call to ``rrule``.
377
+ :param bysetpos:
378
+ If given, it must be either an integer, or a sequence of integers,
379
+ positive or negative. Each given integer will specify an occurrence
380
+ number, corresponding to the nth occurrence of the rule inside the
381
+ frequency period. For example, a bysetpos of -1 if combined with a
382
+ MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will
383
+ result in the last work day of every month.
384
+ :param bymonth:
385
+ If given, it must be either an integer, or a sequence of integers,
386
+ meaning the months to apply the recurrence to.
387
+ :param bymonthday:
388
+ If given, it must be either an integer, or a sequence of integers,
389
+ meaning the month days to apply the recurrence to.
390
+ :param byyearday:
391
+ If given, it must be either an integer, or a sequence of integers,
392
+ meaning the year days to apply the recurrence to.
393
+ :param byeaster:
394
+ If given, it must be either an integer, or a sequence of integers,
395
+ positive or negative. Each integer will define an offset from the
396
+ Easter Sunday. Passing the offset 0 to byeaster will yield the Easter
397
+ Sunday itself. This is an extension to the RFC specification.
398
+ :param byweekno:
399
+ If given, it must be either an integer, or a sequence of integers,
400
+ meaning the week numbers to apply the recurrence to. Week numbers
401
+ have the meaning described in ISO8601, that is, the first week of
402
+ the year is that containing at least four days of the new year.
403
+ :param byweekday:
404
+ If given, it must be either an integer (0 == MO), a sequence of
405
+ integers, one of the weekday constants (MO, TU, etc), or a sequence
406
+ of these constants. When given, these variables will define the
407
+ weekdays where the recurrence will be applied. It's also possible to
408
+ use an argument n for the weekday instances, which will mean the nth
409
+ occurrence of this weekday in the period. For example, with MONTHLY,
410
+ or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the
411
+ first friday of the month where the recurrence happens. Notice that in
412
+ the RFC documentation, this is specified as BYDAY, but was renamed to
413
+ avoid the ambiguity of that keyword.
414
+ :param byhour:
415
+ If given, it must be either an integer, or a sequence of integers,
416
+ meaning the hours to apply the recurrence to.
417
+ :param byminute:
418
+ If given, it must be either an integer, or a sequence of integers,
419
+ meaning the minutes to apply the recurrence to.
420
+ :param bysecond:
421
+ If given, it must be either an integer, or a sequence of integers,
422
+ meaning the seconds to apply the recurrence to.
423
+ :param cache:
424
+ If given, it must be a boolean value specifying to enable or disable
425
+ caching of results. If you will use the same rrule instance multiple
426
+ times, enabling caching will improve the performance considerably.
427
+ """
428
+ def __init__(self, freq, dtstart=None,
429
+ interval=1, wkst=None, count=None, until=None, bysetpos=None,
430
+ bymonth=None, bymonthday=None, byyearday=None, byeaster=None,
431
+ byweekno=None, byweekday=None,
432
+ byhour=None, byminute=None, bysecond=None,
433
+ cache=False):
434
+ super(rrule, self).__init__(cache)
435
+ global easter
436
+ if not dtstart:
437
+ if until and until.tzinfo:
438
+ dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0)
439
+ else:
440
+ dtstart = datetime.datetime.now().replace(microsecond=0)
441
+ elif not isinstance(dtstart, datetime.datetime):
442
+ dtstart = datetime.datetime.fromordinal(dtstart.toordinal())
443
+ else:
444
+ dtstart = dtstart.replace(microsecond=0)
445
+ self._dtstart = dtstart
446
+ self._tzinfo = dtstart.tzinfo
447
+ self._freq = freq
448
+ self._interval = interval
449
+ self._count = count
450
+
451
+ # Cache the original byxxx rules, if they are provided, as the _byxxx
452
+ # attributes do not necessarily map to the inputs, and this can be
453
+ # a problem in generating the strings. Only store things if they've
454
+ # been supplied (the string retrieval will just use .get())
455
+ self._original_rule = {}
456
+
457
+ if until and not isinstance(until, datetime.datetime):
458
+ until = datetime.datetime.fromordinal(until.toordinal())
459
+ self._until = until
460
+
461
+ if self._dtstart and self._until:
462
+ if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None):
463
+ # According to RFC5545 Section 3.3.10:
464
+ # https://tools.ietf.org/html/rfc5545#section-3.3.10
465
+ #
466
+ # > If the "DTSTART" property is specified as a date with UTC
467
+ # > time or a date with local time and time zone reference,
468
+ # > then the UNTIL rule part MUST be specified as a date with
469
+ # > UTC time.
470
+ raise ValueError(
471
+ 'RRULE UNTIL values must be specified in UTC when DTSTART '
472
+ 'is timezone-aware'
473
+ )
474
+
475
+ if count is not None and until:
476
+ warn("Using both 'count' and 'until' is inconsistent with RFC 5545"
477
+ " and has been deprecated in dateutil. Future versions will "
478
+ "raise an error.", DeprecationWarning)
479
+
480
+ if wkst is None:
481
+ self._wkst = calendar.firstweekday()
482
+ elif isinstance(wkst, integer_types):
483
+ self._wkst = wkst
484
+ else:
485
+ self._wkst = wkst.weekday
486
+
487
+ if bysetpos is None:
488
+ self._bysetpos = None
489
+ elif isinstance(bysetpos, integer_types):
490
+ if bysetpos == 0 or not (-366 <= bysetpos <= 366):
491
+ raise ValueError("bysetpos must be between 1 and 366, "
492
+ "or between -366 and -1")
493
+ self._bysetpos = (bysetpos,)
494
+ else:
495
+ self._bysetpos = tuple(bysetpos)
496
+ for pos in self._bysetpos:
497
+ if pos == 0 or not (-366 <= pos <= 366):
498
+ raise ValueError("bysetpos must be between 1 and 366, "
499
+ "or between -366 and -1")
500
+
501
+ if self._bysetpos:
502
+ self._original_rule['bysetpos'] = self._bysetpos
503
+
504
+ if (byweekno is None and byyearday is None and bymonthday is None and
505
+ byweekday is None and byeaster is None):
506
+ if freq == YEARLY:
507
+ if bymonth is None:
508
+ bymonth = dtstart.month
509
+ self._original_rule['bymonth'] = None
510
+ bymonthday = dtstart.day
511
+ self._original_rule['bymonthday'] = None
512
+ elif freq == MONTHLY:
513
+ bymonthday = dtstart.day
514
+ self._original_rule['bymonthday'] = None
515
+ elif freq == WEEKLY:
516
+ byweekday = dtstart.weekday()
517
+ self._original_rule['byweekday'] = None
518
+
519
+ # bymonth
520
+ if bymonth is None:
521
+ self._bymonth = None
522
+ else:
523
+ if isinstance(bymonth, integer_types):
524
+ bymonth = (bymonth,)
525
+
526
+ self._bymonth = tuple(sorted(set(bymonth)))
527
+
528
+ if 'bymonth' not in self._original_rule:
529
+ self._original_rule['bymonth'] = self._bymonth
530
+
531
+ # byyearday
532
+ if byyearday is None:
533
+ self._byyearday = None
534
+ else:
535
+ if isinstance(byyearday, integer_types):
536
+ byyearday = (byyearday,)
537
+
538
+ self._byyearday = tuple(sorted(set(byyearday)))
539
+ self._original_rule['byyearday'] = self._byyearday
540
+
541
+ # byeaster
542
+ if byeaster is not None:
543
+ if not easter:
544
+ from dateutil import easter
545
+ if isinstance(byeaster, integer_types):
546
+ self._byeaster = (byeaster,)
547
+ else:
548
+ self._byeaster = tuple(sorted(byeaster))
549
+
550
+ self._original_rule['byeaster'] = self._byeaster
551
+ else:
552
+ self._byeaster = None
553
+
554
+ # bymonthday
555
+ if bymonthday is None:
556
+ self._bymonthday = ()
557
+ self._bynmonthday = ()
558
+ else:
559
+ if isinstance(bymonthday, integer_types):
560
+ bymonthday = (bymonthday,)
561
+
562
+ bymonthday = set(bymonthday) # Ensure it's unique
563
+
564
+ self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0))
565
+ self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0))
566
+
567
+ # Storing positive numbers first, then negative numbers
568
+ if 'bymonthday' not in self._original_rule:
569
+ self._original_rule['bymonthday'] = tuple(
570
+ itertools.chain(self._bymonthday, self._bynmonthday))
571
+
572
+ # byweekno
573
+ if byweekno is None:
574
+ self._byweekno = None
575
+ else:
576
+ if isinstance(byweekno, integer_types):
577
+ byweekno = (byweekno,)
578
+
579
+ self._byweekno = tuple(sorted(set(byweekno)))
580
+
581
+ self._original_rule['byweekno'] = self._byweekno
582
+
583
+ # byweekday / bynweekday
584
+ if byweekday is None:
585
+ self._byweekday = None
586
+ self._bynweekday = None
587
+ else:
588
+ # If it's one of the valid non-sequence types, convert to a
589
+ # single-element sequence before the iterator that builds the
590
+ # byweekday set.
591
+ if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"):
592
+ byweekday = (byweekday,)
593
+
594
+ self._byweekday = set()
595
+ self._bynweekday = set()
596
+ for wday in byweekday:
597
+ if isinstance(wday, integer_types):
598
+ self._byweekday.add(wday)
599
+ elif not wday.n or freq > MONTHLY:
600
+ self._byweekday.add(wday.weekday)
601
+ else:
602
+ self._bynweekday.add((wday.weekday, wday.n))
603
+
604
+ if not self._byweekday:
605
+ self._byweekday = None
606
+ elif not self._bynweekday:
607
+ self._bynweekday = None
608
+
609
+ if self._byweekday is not None:
610
+ self._byweekday = tuple(sorted(self._byweekday))
611
+ orig_byweekday = [weekday(x) for x in self._byweekday]
612
+ else:
613
+ orig_byweekday = ()
614
+
615
+ if self._bynweekday is not None:
616
+ self._bynweekday = tuple(sorted(self._bynweekday))
617
+ orig_bynweekday = [weekday(*x) for x in self._bynweekday]
618
+ else:
619
+ orig_bynweekday = ()
620
+
621
+ if 'byweekday' not in self._original_rule:
622
+ self._original_rule['byweekday'] = tuple(itertools.chain(
623
+ orig_byweekday, orig_bynweekday))
624
+
625
+ # byhour
626
+ if byhour is None:
627
+ if freq < HOURLY:
628
+ self._byhour = {dtstart.hour}
629
+ else:
630
+ self._byhour = None
631
+ else:
632
+ if isinstance(byhour, integer_types):
633
+ byhour = (byhour,)
634
+
635
+ if freq == HOURLY:
636
+ self._byhour = self.__construct_byset(start=dtstart.hour,
637
+ byxxx=byhour,
638
+ base=24)
639
+ else:
640
+ self._byhour = set(byhour)
641
+
642
+ self._byhour = tuple(sorted(self._byhour))
643
+ self._original_rule['byhour'] = self._byhour
644
+
645
+ # byminute
646
+ if byminute is None:
647
+ if freq < MINUTELY:
648
+ self._byminute = {dtstart.minute}
649
+ else:
650
+ self._byminute = None
651
+ else:
652
+ if isinstance(byminute, integer_types):
653
+ byminute = (byminute,)
654
+
655
+ if freq == MINUTELY:
656
+ self._byminute = self.__construct_byset(start=dtstart.minute,
657
+ byxxx=byminute,
658
+ base=60)
659
+ else:
660
+ self._byminute = set(byminute)
661
+
662
+ self._byminute = tuple(sorted(self._byminute))
663
+ self._original_rule['byminute'] = self._byminute
664
+
665
+ # bysecond
666
+ if bysecond is None:
667
+ if freq < SECONDLY:
668
+ self._bysecond = ((dtstart.second,))
669
+ else:
670
+ self._bysecond = None
671
+ else:
672
+ if isinstance(bysecond, integer_types):
673
+ bysecond = (bysecond,)
674
+
675
+ self._bysecond = set(bysecond)
676
+
677
+ if freq == SECONDLY:
678
+ self._bysecond = self.__construct_byset(start=dtstart.second,
679
+ byxxx=bysecond,
680
+ base=60)
681
+ else:
682
+ self._bysecond = set(bysecond)
683
+
684
+ self._bysecond = tuple(sorted(self._bysecond))
685
+ self._original_rule['bysecond'] = self._bysecond
686
+
687
+ if self._freq >= HOURLY:
688
+ self._timeset = None
689
+ else:
690
+ self._timeset = []
691
+ for hour in self._byhour:
692
+ for minute in self._byminute:
693
+ for second in self._bysecond:
694
+ self._timeset.append(
695
+ datetime.time(hour, minute, second,
696
+ tzinfo=self._tzinfo))
697
+ self._timeset.sort()
698
+ self._timeset = tuple(self._timeset)
699
+
700
+ def __str__(self):
701
+ """
702
+ Output a string that would generate this RRULE if passed to rrulestr.
703
+ This is mostly compatible with RFC5545, except for the
704
+ dateutil-specific extension BYEASTER.
705
+ """
706
+
707
+ output = []
708
+ h, m, s = [None] * 3
709
+ if self._dtstart:
710
+ output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S'))
711
+ h, m, s = self._dtstart.timetuple()[3:6]
712
+
713
+ parts = ['FREQ=' + FREQNAMES[self._freq]]
714
+ if self._interval != 1:
715
+ parts.append('INTERVAL=' + str(self._interval))
716
+
717
+ if self._wkst:
718
+ parts.append('WKST=' + repr(weekday(self._wkst))[0:2])
719
+
720
+ if self._count is not None:
721
+ parts.append('COUNT=' + str(self._count))
722
+
723
+ if self._until:
724
+ parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S'))
725
+
726
+ if self._original_rule.get('byweekday') is not None:
727
+ # The str() method on weekday objects doesn't generate
728
+ # RFC5545-compliant strings, so we should modify that.
729
+ original_rule = dict(self._original_rule)
730
+ wday_strings = []
731
+ for wday in original_rule['byweekday']:
732
+ if wday.n:
733
+ wday_strings.append('{n:+d}{wday}'.format(
734
+ n=wday.n,
735
+ wday=repr(wday)[0:2]))
736
+ else:
737
+ wday_strings.append(repr(wday))
738
+
739
+ original_rule['byweekday'] = wday_strings
740
+ else:
741
+ original_rule = self._original_rule
742
+
743
+ partfmt = '{name}={vals}'
744
+ for name, key in [('BYSETPOS', 'bysetpos'),
745
+ ('BYMONTH', 'bymonth'),
746
+ ('BYMONTHDAY', 'bymonthday'),
747
+ ('BYYEARDAY', 'byyearday'),
748
+ ('BYWEEKNO', 'byweekno'),
749
+ ('BYDAY', 'byweekday'),
750
+ ('BYHOUR', 'byhour'),
751
+ ('BYMINUTE', 'byminute'),
752
+ ('BYSECOND', 'bysecond'),
753
+ ('BYEASTER', 'byeaster')]:
754
+ value = original_rule.get(key)
755
+ if value:
756
+ parts.append(partfmt.format(name=name, vals=(','.join(str(v)
757
+ for v in value))))
758
+
759
+ output.append('RRULE:' + ';'.join(parts))
760
+ return '\n'.join(output)
761
+
762
+ def replace(self, **kwargs):
763
+ """Return new rrule with same attributes except for those attributes given new
764
+ values by whichever keyword arguments are specified."""
765
+ new_kwargs = {"interval": self._interval,
766
+ "count": self._count,
767
+ "dtstart": self._dtstart,
768
+ "freq": self._freq,
769
+ "until": self._until,
770
+ "wkst": self._wkst,
771
+ "cache": False if self._cache is None else True }
772
+ new_kwargs.update(self._original_rule)
773
+ new_kwargs.update(kwargs)
774
+ return rrule(**new_kwargs)
775
+
776
+ def _iter(self):
777
+ year, month, day, hour, minute, second, weekday, yearday, _ = \
778
+ self._dtstart.timetuple()
779
+
780
+ # Some local variables to speed things up a bit
781
+ freq = self._freq
782
+ interval = self._interval
783
+ wkst = self._wkst
784
+ until = self._until
785
+ bymonth = self._bymonth
786
+ byweekno = self._byweekno
787
+ byyearday = self._byyearday
788
+ byweekday = self._byweekday
789
+ byeaster = self._byeaster
790
+ bymonthday = self._bymonthday
791
+ bynmonthday = self._bynmonthday
792
+ bysetpos = self._bysetpos
793
+ byhour = self._byhour
794
+ byminute = self._byminute
795
+ bysecond = self._bysecond
796
+
797
+ ii = _iterinfo(self)
798
+ ii.rebuild(year, month)
799
+
800
+ getdayset = {YEARLY: ii.ydayset,
801
+ MONTHLY: ii.mdayset,
802
+ WEEKLY: ii.wdayset,
803
+ DAILY: ii.ddayset,
804
+ HOURLY: ii.ddayset,
805
+ MINUTELY: ii.ddayset,
806
+ SECONDLY: ii.ddayset}[freq]
807
+
808
+ if freq < HOURLY:
809
+ timeset = self._timeset
810
+ else:
811
+ gettimeset = {HOURLY: ii.htimeset,
812
+ MINUTELY: ii.mtimeset,
813
+ SECONDLY: ii.stimeset}[freq]
814
+ if ((freq >= HOURLY and
815
+ self._byhour and hour not in self._byhour) or
816
+ (freq >= MINUTELY and
817
+ self._byminute and minute not in self._byminute) or
818
+ (freq >= SECONDLY and
819
+ self._bysecond and second not in self._bysecond)):
820
+ timeset = ()
821
+ else:
822
+ timeset = gettimeset(hour, minute, second)
823
+
824
+ total = 0
825
+ count = self._count
826
+ while True:
827
+ # Get dayset with the right frequency
828
+ dayset, start, end = getdayset(year, month, day)
829
+
830
+ # Do the "hard" work ;-)
831
+ filtered = False
832
+ for i in dayset[start:end]:
833
+ if ((bymonth and ii.mmask[i] not in bymonth) or
834
+ (byweekno and not ii.wnomask[i]) or
835
+ (byweekday and ii.wdaymask[i] not in byweekday) or
836
+ (ii.nwdaymask and not ii.nwdaymask[i]) or
837
+ (byeaster and not ii.eastermask[i]) or
838
+ ((bymonthday or bynmonthday) and
839
+ ii.mdaymask[i] not in bymonthday and
840
+ ii.nmdaymask[i] not in bynmonthday) or
841
+ (byyearday and
842
+ ((i < ii.yearlen and i+1 not in byyearday and
843
+ -ii.yearlen+i not in byyearday) or
844
+ (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and
845
+ -ii.nextyearlen+i-ii.yearlen not in byyearday)))):
846
+ dayset[i] = None
847
+ filtered = True
848
+
849
+ # Output results
850
+ if bysetpos and timeset:
851
+ poslist = []
852
+ for pos in bysetpos:
853
+ if pos < 0:
854
+ daypos, timepos = divmod(pos, len(timeset))
855
+ else:
856
+ daypos, timepos = divmod(pos-1, len(timeset))
857
+ try:
858
+ i = [x for x in dayset[start:end]
859
+ if x is not None][daypos]
860
+ time = timeset[timepos]
861
+ except IndexError:
862
+ pass
863
+ else:
864
+ date = datetime.date.fromordinal(ii.yearordinal+i)
865
+ res = datetime.datetime.combine(date, time)
866
+ if res not in poslist:
867
+ poslist.append(res)
868
+ poslist.sort()
869
+ for res in poslist:
870
+ if until and res > until:
871
+ self._len = total
872
+ return
873
+ elif res >= self._dtstart:
874
+ if count is not None:
875
+ count -= 1
876
+ if count < 0:
877
+ self._len = total
878
+ return
879
+ total += 1
880
+ yield res
881
+ else:
882
+ for i in dayset[start:end]:
883
+ if i is not None:
884
+ date = datetime.date.fromordinal(ii.yearordinal + i)
885
+ for time in timeset:
886
+ res = datetime.datetime.combine(date, time)
887
+ if until and res > until:
888
+ self._len = total
889
+ return
890
+ elif res >= self._dtstart:
891
+ if count is not None:
892
+ count -= 1
893
+ if count < 0:
894
+ self._len = total
895
+ return
896
+
897
+ total += 1
898
+ yield res
899
+
900
+ # Handle frequency and interval
901
+ fixday = False
902
+ if freq == YEARLY:
903
+ year += interval
904
+ if year > datetime.MAXYEAR:
905
+ self._len = total
906
+ return
907
+ ii.rebuild(year, month)
908
+ elif freq == MONTHLY:
909
+ month += interval
910
+ if month > 12:
911
+ div, mod = divmod(month, 12)
912
+ month = mod
913
+ year += div
914
+ if month == 0:
915
+ month = 12
916
+ year -= 1
917
+ if year > datetime.MAXYEAR:
918
+ self._len = total
919
+ return
920
+ ii.rebuild(year, month)
921
+ elif freq == WEEKLY:
922
+ if wkst > weekday:
923
+ day += -(weekday+1+(6-wkst))+self._interval*7
924
+ else:
925
+ day += -(weekday-wkst)+self._interval*7
926
+ weekday = wkst
927
+ fixday = True
928
+ elif freq == DAILY:
929
+ day += interval
930
+ fixday = True
931
+ elif freq == HOURLY:
932
+ if filtered:
933
+ # Jump to one iteration before next day
934
+ hour += ((23-hour)//interval)*interval
935
+
936
+ if byhour:
937
+ ndays, hour = self.__mod_distance(value=hour,
938
+ byxxx=self._byhour,
939
+ base=24)
940
+ else:
941
+ ndays, hour = divmod(hour+interval, 24)
942
+
943
+ if ndays:
944
+ day += ndays
945
+ fixday = True
946
+
947
+ timeset = gettimeset(hour, minute, second)
948
+ elif freq == MINUTELY:
949
+ if filtered:
950
+ # Jump to one iteration before next day
951
+ minute += ((1439-(hour*60+minute))//interval)*interval
952
+
953
+ valid = False
954
+ rep_rate = (24*60)
955
+ for j in range(rep_rate // gcd(interval, rep_rate)):
956
+ if byminute:
957
+ nhours, minute = \
958
+ self.__mod_distance(value=minute,
959
+ byxxx=self._byminute,
960
+ base=60)
961
+ else:
962
+ nhours, minute = divmod(minute+interval, 60)
963
+
964
+ div, hour = divmod(hour+nhours, 24)
965
+ if div:
966
+ day += div
967
+ fixday = True
968
+ filtered = False
969
+
970
+ if not byhour or hour in byhour:
971
+ valid = True
972
+ break
973
+
974
+ if not valid:
975
+ raise ValueError('Invalid combination of interval and ' +
976
+ 'byhour resulting in empty rule.')
977
+
978
+ timeset = gettimeset(hour, minute, second)
979
+ elif freq == SECONDLY:
980
+ if filtered:
981
+ # Jump to one iteration before next day
982
+ second += (((86399 - (hour * 3600 + minute * 60 + second))
983
+ // interval) * interval)
984
+
985
+ rep_rate = (24 * 3600)
986
+ valid = False
987
+ for j in range(0, rep_rate // gcd(interval, rep_rate)):
988
+ if bysecond:
989
+ nminutes, second = \
990
+ self.__mod_distance(value=second,
991
+ byxxx=self._bysecond,
992
+ base=60)
993
+ else:
994
+ nminutes, second = divmod(second+interval, 60)
995
+
996
+ div, minute = divmod(minute+nminutes, 60)
997
+ if div:
998
+ hour += div
999
+ div, hour = divmod(hour, 24)
1000
+ if div:
1001
+ day += div
1002
+ fixday = True
1003
+
1004
+ if ((not byhour or hour in byhour) and
1005
+ (not byminute or minute in byminute) and
1006
+ (not bysecond or second in bysecond)):
1007
+ valid = True
1008
+ break
1009
+
1010
+ if not valid:
1011
+ raise ValueError('Invalid combination of interval, ' +
1012
+ 'byhour and byminute resulting in empty' +
1013
+ ' rule.')
1014
+
1015
+ timeset = gettimeset(hour, minute, second)
1016
+
1017
+ if fixday and day > 28:
1018
+ daysinmonth = calendar.monthrange(year, month)[1]
1019
+ if day > daysinmonth:
1020
+ while day > daysinmonth:
1021
+ day -= daysinmonth
1022
+ month += 1
1023
+ if month == 13:
1024
+ month = 1
1025
+ year += 1
1026
+ if year > datetime.MAXYEAR:
1027
+ self._len = total
1028
+ return
1029
+ daysinmonth = calendar.monthrange(year, month)[1]
1030
+ ii.rebuild(year, month)
1031
+
1032
+ def __construct_byset(self, start, byxxx, base):
1033
+ """
1034
+ If a `BYXXX` sequence is passed to the constructor at the same level as
1035
+ `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some
1036
+ specifications which cannot be reached given some starting conditions.
1037
+
1038
+ This occurs whenever the interval is not coprime with the base of a
1039
+ given unit and the difference between the starting position and the
1040
+ ending position is not coprime with the greatest common denominator
1041
+ between the interval and the base. For example, with a FREQ of hourly
1042
+ starting at 17:00 and an interval of 4, the only valid values for
1043
+ BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not
1044
+ coprime.
1045
+
1046
+ :param start:
1047
+ Specifies the starting position.
1048
+ :param byxxx:
1049
+ An iterable containing the list of allowed values.
1050
+ :param base:
1051
+ The largest allowable value for the specified frequency (e.g.
1052
+ 24 hours, 60 minutes).
1053
+
1054
+ This does not preserve the type of the iterable, returning a set, since
1055
+ the values should be unique and the order is irrelevant, this will
1056
+ speed up later lookups.
1057
+
1058
+ In the event of an empty set, raises a :exception:`ValueError`, as this
1059
+ results in an empty rrule.
1060
+ """
1061
+
1062
+ cset = set()
1063
+
1064
+ # Support a single byxxx value.
1065
+ if isinstance(byxxx, integer_types):
1066
+ byxxx = (byxxx, )
1067
+
1068
+ for num in byxxx:
1069
+ i_gcd = gcd(self._interval, base)
1070
+ # Use divmod rather than % because we need to wrap negative nums.
1071
+ if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0:
1072
+ cset.add(num)
1073
+
1074
+ if len(cset) == 0:
1075
+ raise ValueError("Invalid rrule byxxx generates an empty set.")
1076
+
1077
+ return cset
1078
+
1079
+ def __mod_distance(self, value, byxxx, base):
1080
+ """
1081
+ Calculates the next value in a sequence where the `FREQ` parameter is
1082
+ specified along with a `BYXXX` parameter at the same "level"
1083
+ (e.g. `HOURLY` specified with `BYHOUR`).
1084
+
1085
+ :param value:
1086
+ The old value of the component.
1087
+ :param byxxx:
1088
+ The `BYXXX` set, which should have been generated by
1089
+ `rrule._construct_byset`, or something else which checks that a
1090
+ valid rule is present.
1091
+ :param base:
1092
+ The largest allowable value for the specified frequency (e.g.
1093
+ 24 hours, 60 minutes).
1094
+
1095
+ If a valid value is not found after `base` iterations (the maximum
1096
+ number before the sequence would start to repeat), this raises a
1097
+ :exception:`ValueError`, as no valid values were found.
1098
+
1099
+ This returns a tuple of `divmod(n*interval, base)`, where `n` is the
1100
+ smallest number of `interval` repetitions until the next specified
1101
+ value in `byxxx` is found.
1102
+ """
1103
+ accumulator = 0
1104
+ for ii in range(1, base + 1):
1105
+ # Using divmod() over % to account for negative intervals
1106
+ div, value = divmod(value + self._interval, base)
1107
+ accumulator += div
1108
+ if value in byxxx:
1109
+ return (accumulator, value)
1110
+
1111
+
1112
+ class _iterinfo(object):
1113
+ __slots__ = ["rrule", "lastyear", "lastmonth",
1114
+ "yearlen", "nextyearlen", "yearordinal", "yearweekday",
1115
+ "mmask", "mrange", "mdaymask", "nmdaymask",
1116
+ "wdaymask", "wnomask", "nwdaymask", "eastermask"]
1117
+
1118
+ def __init__(self, rrule):
1119
+ for attr in self.__slots__:
1120
+ setattr(self, attr, None)
1121
+ self.rrule = rrule
1122
+
1123
+ def rebuild(self, year, month):
1124
+ # Every mask is 7 days longer to handle cross-year weekly periods.
1125
+ rr = self.rrule
1126
+ if year != self.lastyear:
1127
+ self.yearlen = 365 + calendar.isleap(year)
1128
+ self.nextyearlen = 365 + calendar.isleap(year + 1)
1129
+ firstyday = datetime.date(year, 1, 1)
1130
+ self.yearordinal = firstyday.toordinal()
1131
+ self.yearweekday = firstyday.weekday()
1132
+
1133
+ wday = datetime.date(year, 1, 1).weekday()
1134
+ if self.yearlen == 365:
1135
+ self.mmask = M365MASK
1136
+ self.mdaymask = MDAY365MASK
1137
+ self.nmdaymask = NMDAY365MASK
1138
+ self.wdaymask = WDAYMASK[wday:]
1139
+ self.mrange = M365RANGE
1140
+ else:
1141
+ self.mmask = M366MASK
1142
+ self.mdaymask = MDAY366MASK
1143
+ self.nmdaymask = NMDAY366MASK
1144
+ self.wdaymask = WDAYMASK[wday:]
1145
+ self.mrange = M366RANGE
1146
+
1147
+ if not rr._byweekno:
1148
+ self.wnomask = None
1149
+ else:
1150
+ self.wnomask = [0]*(self.yearlen+7)
1151
+ # no1wkst = firstwkst = self.wdaymask.index(rr._wkst)
1152
+ no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7
1153
+ if no1wkst >= 4:
1154
+ no1wkst = 0
1155
+ # Number of days in the year, plus the days we got
1156
+ # from last year.
1157
+ wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7
1158
+ else:
1159
+ # Number of days in the year, minus the days we
1160
+ # left in last year.
1161
+ wyearlen = self.yearlen-no1wkst
1162
+ div, mod = divmod(wyearlen, 7)
1163
+ numweeks = div+mod//4
1164
+ for n in rr._byweekno:
1165
+ if n < 0:
1166
+ n += numweeks+1
1167
+ if not (0 < n <= numweeks):
1168
+ continue
1169
+ if n > 1:
1170
+ i = no1wkst+(n-1)*7
1171
+ if no1wkst != firstwkst:
1172
+ i -= 7-firstwkst
1173
+ else:
1174
+ i = no1wkst
1175
+ for j in range(7):
1176
+ self.wnomask[i] = 1
1177
+ i += 1
1178
+ if self.wdaymask[i] == rr._wkst:
1179
+ break
1180
+ if 1 in rr._byweekno:
1181
+ # Check week number 1 of next year as well
1182
+ # TODO: Check -numweeks for next year.
1183
+ i = no1wkst+numweeks*7
1184
+ if no1wkst != firstwkst:
1185
+ i -= 7-firstwkst
1186
+ if i < self.yearlen:
1187
+ # If week starts in next year, we
1188
+ # don't care about it.
1189
+ for j in range(7):
1190
+ self.wnomask[i] = 1
1191
+ i += 1
1192
+ if self.wdaymask[i] == rr._wkst:
1193
+ break
1194
+ if no1wkst:
1195
+ # Check last week number of last year as
1196
+ # well. If no1wkst is 0, either the year
1197
+ # started on week start, or week number 1
1198
+ # got days from last year, so there are no
1199
+ # days from last year's last week number in
1200
+ # this year.
1201
+ if -1 not in rr._byweekno:
1202
+ lyearweekday = datetime.date(year-1, 1, 1).weekday()
1203
+ lno1wkst = (7-lyearweekday+rr._wkst) % 7
1204
+ lyearlen = 365+calendar.isleap(year-1)
1205
+ if lno1wkst >= 4:
1206
+ lno1wkst = 0
1207
+ lnumweeks = 52+(lyearlen +
1208
+ (lyearweekday-rr._wkst) % 7) % 7//4
1209
+ else:
1210
+ lnumweeks = 52+(self.yearlen-no1wkst) % 7//4
1211
+ else:
1212
+ lnumweeks = -1
1213
+ if lnumweeks in rr._byweekno:
1214
+ for i in range(no1wkst):
1215
+ self.wnomask[i] = 1
1216
+
1217
+ if (rr._bynweekday and (month != self.lastmonth or
1218
+ year != self.lastyear)):
1219
+ ranges = []
1220
+ if rr._freq == YEARLY:
1221
+ if rr._bymonth:
1222
+ for month in rr._bymonth:
1223
+ ranges.append(self.mrange[month-1:month+1])
1224
+ else:
1225
+ ranges = [(0, self.yearlen)]
1226
+ elif rr._freq == MONTHLY:
1227
+ ranges = [self.mrange[month-1:month+1]]
1228
+ if ranges:
1229
+ # Weekly frequency won't get here, so we may not
1230
+ # care about cross-year weekly periods.
1231
+ self.nwdaymask = [0]*self.yearlen
1232
+ for first, last in ranges:
1233
+ last -= 1
1234
+ for wday, n in rr._bynweekday:
1235
+ if n < 0:
1236
+ i = last+(n+1)*7
1237
+ i -= (self.wdaymask[i]-wday) % 7
1238
+ else:
1239
+ i = first+(n-1)*7
1240
+ i += (7-self.wdaymask[i]+wday) % 7
1241
+ if first <= i <= last:
1242
+ self.nwdaymask[i] = 1
1243
+
1244
+ if rr._byeaster:
1245
+ self.eastermask = [0]*(self.yearlen+7)
1246
+ eyday = easter.easter(year).toordinal()-self.yearordinal
1247
+ for offset in rr._byeaster:
1248
+ self.eastermask[eyday+offset] = 1
1249
+
1250
+ self.lastyear = year
1251
+ self.lastmonth = month
1252
+
1253
+ def ydayset(self, year, month, day):
1254
+ return list(range(self.yearlen)), 0, self.yearlen
1255
+
1256
+ def mdayset(self, year, month, day):
1257
+ dset = [None]*self.yearlen
1258
+ start, end = self.mrange[month-1:month+1]
1259
+ for i in range(start, end):
1260
+ dset[i] = i
1261
+ return dset, start, end
1262
+
1263
+ def wdayset(self, year, month, day):
1264
+ # We need to handle cross-year weeks here.
1265
+ dset = [None]*(self.yearlen+7)
1266
+ i = datetime.date(year, month, day).toordinal()-self.yearordinal
1267
+ start = i
1268
+ for j in range(7):
1269
+ dset[i] = i
1270
+ i += 1
1271
+ # if (not (0 <= i < self.yearlen) or
1272
+ # self.wdaymask[i] == self.rrule._wkst):
1273
+ # This will cross the year boundary, if necessary.
1274
+ if self.wdaymask[i] == self.rrule._wkst:
1275
+ break
1276
+ return dset, start, i
1277
+
1278
+ def ddayset(self, year, month, day):
1279
+ dset = [None] * self.yearlen
1280
+ i = datetime.date(year, month, day).toordinal() - self.yearordinal
1281
+ dset[i] = i
1282
+ return dset, i, i + 1
1283
+
1284
+ def htimeset(self, hour, minute, second):
1285
+ tset = []
1286
+ rr = self.rrule
1287
+ for minute in rr._byminute:
1288
+ for second in rr._bysecond:
1289
+ tset.append(datetime.time(hour, minute, second,
1290
+ tzinfo=rr._tzinfo))
1291
+ tset.sort()
1292
+ return tset
1293
+
1294
+ def mtimeset(self, hour, minute, second):
1295
+ tset = []
1296
+ rr = self.rrule
1297
+ for second in rr._bysecond:
1298
+ tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo))
1299
+ tset.sort()
1300
+ return tset
1301
+
1302
+ def stimeset(self, hour, minute, second):
1303
+ return (datetime.time(hour, minute, second,
1304
+ tzinfo=self.rrule._tzinfo),)
1305
+
1306
+
1307
+ class rruleset(rrulebase):
1308
+ """ The rruleset type allows more complex recurrence setups, mixing
1309
+ multiple rules, dates, exclusion rules, and exclusion dates. The type
1310
+ constructor takes the following keyword arguments:
1311
+
1312
+ :param cache: If True, caching of results will be enabled, improving
1313
+ performance of multiple queries considerably. """
1314
+
1315
+ class _genitem(object):
1316
+ def __init__(self, genlist, gen):
1317
+ try:
1318
+ self.dt = advance_iterator(gen)
1319
+ genlist.append(self)
1320
+ except StopIteration:
1321
+ pass
1322
+ self.genlist = genlist
1323
+ self.gen = gen
1324
+
1325
+ def __next__(self):
1326
+ try:
1327
+ self.dt = advance_iterator(self.gen)
1328
+ except StopIteration:
1329
+ if self.genlist[0] is self:
1330
+ heapq.heappop(self.genlist)
1331
+ else:
1332
+ self.genlist.remove(self)
1333
+ heapq.heapify(self.genlist)
1334
+
1335
+ next = __next__
1336
+
1337
+ def __lt__(self, other):
1338
+ return self.dt < other.dt
1339
+
1340
+ def __gt__(self, other):
1341
+ return self.dt > other.dt
1342
+
1343
+ def __eq__(self, other):
1344
+ return self.dt == other.dt
1345
+
1346
+ def __ne__(self, other):
1347
+ return self.dt != other.dt
1348
+
1349
+ def __init__(self, cache=False):
1350
+ super(rruleset, self).__init__(cache)
1351
+ self._rrule = []
1352
+ self._rdate = []
1353
+ self._exrule = []
1354
+ self._exdate = []
1355
+
1356
+ @_invalidates_cache
1357
+ def rrule(self, rrule):
1358
+ """ Include the given :py:class:`rrule` instance in the recurrence set
1359
+ generation. """
1360
+ self._rrule.append(rrule)
1361
+
1362
+ @_invalidates_cache
1363
+ def rdate(self, rdate):
1364
+ """ Include the given :py:class:`datetime` instance in the recurrence
1365
+ set generation. """
1366
+ self._rdate.append(rdate)
1367
+
1368
+ @_invalidates_cache
1369
+ def exrule(self, exrule):
1370
+ """ Include the given rrule instance in the recurrence set exclusion
1371
+ list. Dates which are part of the given recurrence rules will not
1372
+ be generated, even if some inclusive rrule or rdate matches them.
1373
+ """
1374
+ self._exrule.append(exrule)
1375
+
1376
+ @_invalidates_cache
1377
+ def exdate(self, exdate):
1378
+ """ Include the given datetime instance in the recurrence set
1379
+ exclusion list. Dates included that way will not be generated,
1380
+ even if some inclusive rrule or rdate matches them. """
1381
+ self._exdate.append(exdate)
1382
+
1383
+ def _iter(self):
1384
+ rlist = []
1385
+ self._rdate.sort()
1386
+ self._genitem(rlist, iter(self._rdate))
1387
+ for gen in [iter(x) for x in self._rrule]:
1388
+ self._genitem(rlist, gen)
1389
+ exlist = []
1390
+ self._exdate.sort()
1391
+ self._genitem(exlist, iter(self._exdate))
1392
+ for gen in [iter(x) for x in self._exrule]:
1393
+ self._genitem(exlist, gen)
1394
+ lastdt = None
1395
+ total = 0
1396
+ heapq.heapify(rlist)
1397
+ heapq.heapify(exlist)
1398
+ while rlist:
1399
+ ritem = rlist[0]
1400
+ if not lastdt or lastdt != ritem.dt:
1401
+ while exlist and exlist[0] < ritem:
1402
+ exitem = exlist[0]
1403
+ advance_iterator(exitem)
1404
+ if exlist and exlist[0] is exitem:
1405
+ heapq.heapreplace(exlist, exitem)
1406
+ if not exlist or ritem != exlist[0]:
1407
+ total += 1
1408
+ yield ritem.dt
1409
+ lastdt = ritem.dt
1410
+ advance_iterator(ritem)
1411
+ if rlist and rlist[0] is ritem:
1412
+ heapq.heapreplace(rlist, ritem)
1413
+ self._len = total
1414
+
1415
+
1416
+
1417
+
1418
+ class _rrulestr(object):
1419
+ """ Parses a string representation of a recurrence rule or set of
1420
+ recurrence rules.
1421
+
1422
+ :param s:
1423
+ Required, a string defining one or more recurrence rules.
1424
+
1425
+ :param dtstart:
1426
+ If given, used as the default recurrence start if not specified in the
1427
+ rule string.
1428
+
1429
+ :param cache:
1430
+ If set ``True`` caching of results will be enabled, improving
1431
+ performance of multiple queries considerably.
1432
+
1433
+ :param unfold:
1434
+ If set ``True`` indicates that a rule string is split over more
1435
+ than one line and should be joined before processing.
1436
+
1437
+ :param forceset:
1438
+ If set ``True`` forces a :class:`dateutil.rrule.rruleset` to
1439
+ be returned.
1440
+
1441
+ :param compatible:
1442
+ If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``.
1443
+
1444
+ :param ignoretz:
1445
+ If set ``True``, time zones in parsed strings are ignored and a naive
1446
+ :class:`datetime.datetime` object is returned.
1447
+
1448
+ :param tzids:
1449
+ If given, a callable or mapping used to retrieve a
1450
+ :class:`datetime.tzinfo` from a string representation.
1451
+ Defaults to :func:`dateutil.tz.gettz`.
1452
+
1453
+ :param tzinfos:
1454
+ Additional time zone names / aliases which may be present in a string
1455
+ representation. See :func:`dateutil.parser.parse` for more
1456
+ information.
1457
+
1458
+ :return:
1459
+ Returns a :class:`dateutil.rrule.rruleset` or
1460
+ :class:`dateutil.rrule.rrule`
1461
+ """
1462
+
1463
+ _freq_map = {"YEARLY": YEARLY,
1464
+ "MONTHLY": MONTHLY,
1465
+ "WEEKLY": WEEKLY,
1466
+ "DAILY": DAILY,
1467
+ "HOURLY": HOURLY,
1468
+ "MINUTELY": MINUTELY,
1469
+ "SECONDLY": SECONDLY}
1470
+
1471
+ _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3,
1472
+ "FR": 4, "SA": 5, "SU": 6}
1473
+
1474
+ def _handle_int(self, rrkwargs, name, value, **kwargs):
1475
+ rrkwargs[name.lower()] = int(value)
1476
+
1477
+ def _handle_int_list(self, rrkwargs, name, value, **kwargs):
1478
+ rrkwargs[name.lower()] = [int(x) for x in value.split(',')]
1479
+
1480
+ _handle_INTERVAL = _handle_int
1481
+ _handle_COUNT = _handle_int
1482
+ _handle_BYSETPOS = _handle_int_list
1483
+ _handle_BYMONTH = _handle_int_list
1484
+ _handle_BYMONTHDAY = _handle_int_list
1485
+ _handle_BYYEARDAY = _handle_int_list
1486
+ _handle_BYEASTER = _handle_int_list
1487
+ _handle_BYWEEKNO = _handle_int_list
1488
+ _handle_BYHOUR = _handle_int_list
1489
+ _handle_BYMINUTE = _handle_int_list
1490
+ _handle_BYSECOND = _handle_int_list
1491
+
1492
+ def _handle_FREQ(self, rrkwargs, name, value, **kwargs):
1493
+ rrkwargs["freq"] = self._freq_map[value]
1494
+
1495
+ def _handle_UNTIL(self, rrkwargs, name, value, **kwargs):
1496
+ global parser
1497
+ if not parser:
1498
+ from dateutil import parser
1499
+ try:
1500
+ rrkwargs["until"] = parser.parse(value,
1501
+ ignoretz=kwargs.get("ignoretz"),
1502
+ tzinfos=kwargs.get("tzinfos"))
1503
+ except ValueError:
1504
+ raise ValueError("invalid until date")
1505
+
1506
+ def _handle_WKST(self, rrkwargs, name, value, **kwargs):
1507
+ rrkwargs["wkst"] = self._weekday_map[value]
1508
+
1509
+ def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs):
1510
+ """
1511
+ Two ways to specify this: +1MO or MO(+1)
1512
+ """
1513
+ l = []
1514
+ for wday in value.split(','):
1515
+ if '(' in wday:
1516
+ # If it's of the form TH(+1), etc.
1517
+ splt = wday.split('(')
1518
+ w = splt[0]
1519
+ n = int(splt[1][:-1])
1520
+ elif len(wday):
1521
+ # If it's of the form +1MO
1522
+ for i in range(len(wday)):
1523
+ if wday[i] not in '+-0123456789':
1524
+ break
1525
+ n = wday[:i] or None
1526
+ w = wday[i:]
1527
+ if n:
1528
+ n = int(n)
1529
+ else:
1530
+ raise ValueError("Invalid (empty) BYDAY specification.")
1531
+
1532
+ l.append(weekdays[self._weekday_map[w]](n))
1533
+ rrkwargs["byweekday"] = l
1534
+
1535
+ _handle_BYDAY = _handle_BYWEEKDAY
1536
+
1537
+ def _parse_rfc_rrule(self, line,
1538
+ dtstart=None,
1539
+ cache=False,
1540
+ ignoretz=False,
1541
+ tzinfos=None):
1542
+ if line.find(':') != -1:
1543
+ name, value = line.split(':')
1544
+ if name != "RRULE":
1545
+ raise ValueError("unknown parameter name")
1546
+ else:
1547
+ value = line
1548
+ rrkwargs = {}
1549
+ for pair in value.split(';'):
1550
+ name, value = pair.split('=')
1551
+ name = name.upper()
1552
+ value = value.upper()
1553
+ try:
1554
+ getattr(self, "_handle_"+name)(rrkwargs, name, value,
1555
+ ignoretz=ignoretz,
1556
+ tzinfos=tzinfos)
1557
+ except AttributeError:
1558
+ raise ValueError("unknown parameter '%s'" % name)
1559
+ except (KeyError, ValueError):
1560
+ raise ValueError("invalid '%s': %s" % (name, value))
1561
+ return rrule(dtstart=dtstart, cache=cache, **rrkwargs)
1562
+
1563
+ def _parse_date_value(self, date_value, parms, rule_tzids,
1564
+ ignoretz, tzids, tzinfos):
1565
+ global parser
1566
+ if not parser:
1567
+ from dateutil import parser
1568
+
1569
+ datevals = []
1570
+ value_found = False
1571
+ TZID = None
1572
+
1573
+ for parm in parms:
1574
+ if parm.startswith("TZID="):
1575
+ try:
1576
+ tzkey = rule_tzids[parm.split('TZID=')[-1]]
1577
+ except KeyError:
1578
+ continue
1579
+ if tzids is None:
1580
+ from . import tz
1581
+ tzlookup = tz.gettz
1582
+ elif callable(tzids):
1583
+ tzlookup = tzids
1584
+ else:
1585
+ tzlookup = getattr(tzids, 'get', None)
1586
+ if tzlookup is None:
1587
+ msg = ('tzids must be a callable, mapping, or None, '
1588
+ 'not %s' % tzids)
1589
+ raise ValueError(msg)
1590
+
1591
+ TZID = tzlookup(tzkey)
1592
+ continue
1593
+
1594
+ # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found
1595
+ # only once.
1596
+ if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}:
1597
+ raise ValueError("unsupported parm: " + parm)
1598
+ else:
1599
+ if value_found:
1600
+ msg = ("Duplicate value parameter found in: " + parm)
1601
+ raise ValueError(msg)
1602
+ value_found = True
1603
+
1604
+ for datestr in date_value.split(','):
1605
+ date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos)
1606
+ if TZID is not None:
1607
+ if date.tzinfo is None:
1608
+ date = date.replace(tzinfo=TZID)
1609
+ else:
1610
+ raise ValueError('DTSTART/EXDATE specifies multiple timezone')
1611
+ datevals.append(date)
1612
+
1613
+ return datevals
1614
+
1615
+ def _parse_rfc(self, s,
1616
+ dtstart=None,
1617
+ cache=False,
1618
+ unfold=False,
1619
+ forceset=False,
1620
+ compatible=False,
1621
+ ignoretz=False,
1622
+ tzids=None,
1623
+ tzinfos=None):
1624
+ global parser
1625
+ if compatible:
1626
+ forceset = True
1627
+ unfold = True
1628
+
1629
+ TZID_NAMES = dict(map(
1630
+ lambda x: (x.upper(), x),
1631
+ re.findall('TZID=(?P<name>[^:]+):', s)
1632
+ ))
1633
+ s = s.upper()
1634
+ if not s.strip():
1635
+ raise ValueError("empty string")
1636
+ if unfold:
1637
+ lines = s.splitlines()
1638
+ i = 0
1639
+ while i < len(lines):
1640
+ line = lines[i].rstrip()
1641
+ if not line:
1642
+ del lines[i]
1643
+ elif i > 0 and line[0] == " ":
1644
+ lines[i-1] += line[1:]
1645
+ del lines[i]
1646
+ else:
1647
+ i += 1
1648
+ else:
1649
+ lines = s.split()
1650
+ if (not forceset and len(lines) == 1 and (s.find(':') == -1 or
1651
+ s.startswith('RRULE:'))):
1652
+ return self._parse_rfc_rrule(lines[0], cache=cache,
1653
+ dtstart=dtstart, ignoretz=ignoretz,
1654
+ tzinfos=tzinfos)
1655
+ else:
1656
+ rrulevals = []
1657
+ rdatevals = []
1658
+ exrulevals = []
1659
+ exdatevals = []
1660
+ for line in lines:
1661
+ if not line:
1662
+ continue
1663
+ if line.find(':') == -1:
1664
+ name = "RRULE"
1665
+ value = line
1666
+ else:
1667
+ name, value = line.split(':', 1)
1668
+ parms = name.split(';')
1669
+ if not parms:
1670
+ raise ValueError("empty property name")
1671
+ name = parms[0]
1672
+ parms = parms[1:]
1673
+ if name == "RRULE":
1674
+ for parm in parms:
1675
+ raise ValueError("unsupported RRULE parm: "+parm)
1676
+ rrulevals.append(value)
1677
+ elif name == "RDATE":
1678
+ for parm in parms:
1679
+ if parm != "VALUE=DATE-TIME":
1680
+ raise ValueError("unsupported RDATE parm: "+parm)
1681
+ rdatevals.append(value)
1682
+ elif name == "EXRULE":
1683
+ for parm in parms:
1684
+ raise ValueError("unsupported EXRULE parm: "+parm)
1685
+ exrulevals.append(value)
1686
+ elif name == "EXDATE":
1687
+ exdatevals.extend(
1688
+ self._parse_date_value(value, parms,
1689
+ TZID_NAMES, ignoretz,
1690
+ tzids, tzinfos)
1691
+ )
1692
+ elif name == "DTSTART":
1693
+ dtvals = self._parse_date_value(value, parms, TZID_NAMES,
1694
+ ignoretz, tzids, tzinfos)
1695
+ if len(dtvals) != 1:
1696
+ raise ValueError("Multiple DTSTART values specified:" +
1697
+ value)
1698
+ dtstart = dtvals[0]
1699
+ else:
1700
+ raise ValueError("unsupported property: "+name)
1701
+ if (forceset or len(rrulevals) > 1 or rdatevals
1702
+ or exrulevals or exdatevals):
1703
+ if not parser and (rdatevals or exdatevals):
1704
+ from dateutil import parser
1705
+ rset = rruleset(cache=cache)
1706
+ for value in rrulevals:
1707
+ rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart,
1708
+ ignoretz=ignoretz,
1709
+ tzinfos=tzinfos))
1710
+ for value in rdatevals:
1711
+ for datestr in value.split(','):
1712
+ rset.rdate(parser.parse(datestr,
1713
+ ignoretz=ignoretz,
1714
+ tzinfos=tzinfos))
1715
+ for value in exrulevals:
1716
+ rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart,
1717
+ ignoretz=ignoretz,
1718
+ tzinfos=tzinfos))
1719
+ for value in exdatevals:
1720
+ rset.exdate(value)
1721
+ if compatible and dtstart:
1722
+ rset.rdate(dtstart)
1723
+ return rset
1724
+ else:
1725
+ return self._parse_rfc_rrule(rrulevals[0],
1726
+ dtstart=dtstart,
1727
+ cache=cache,
1728
+ ignoretz=ignoretz,
1729
+ tzinfos=tzinfos)
1730
+
1731
+ def __call__(self, s, **kwargs):
1732
+ return self._parse_rfc(s, **kwargs)
1733
+
1734
+
1735
+ rrulestr = _rrulestr()
1736
+
1737
+ # vim:ts=4:sw=4:et
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/tzwin.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # tzwin has moved to dateutil.tz.win
2
+ from .tz.win import *
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/dateutil/utils.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module offers general convenience and utility functions for dealing with
4
+ datetimes.
5
+
6
+ .. versionadded:: 2.7.0
7
+ """
8
+ from __future__ import unicode_literals
9
+
10
+ from datetime import datetime, time
11
+
12
+
13
+ def today(tzinfo=None):
14
+ """
15
+ Returns a :py:class:`datetime` representing the current day at midnight
16
+
17
+ :param tzinfo:
18
+ The time zone to attach (also used to determine the current day).
19
+
20
+ :return:
21
+ A :py:class:`datetime.datetime` object representing the current day
22
+ at midnight.
23
+ """
24
+
25
+ dt = datetime.now(tzinfo)
26
+ return datetime.combine(dt.date(), time(0, tzinfo=tzinfo))
27
+
28
+
29
+ def default_tzinfo(dt, tzinfo):
30
+ """
31
+ Sets the ``tzinfo`` parameter on naive datetimes only
32
+
33
+ This is useful for example when you are provided a datetime that may have
34
+ either an implicit or explicit time zone, such as when parsing a time zone
35
+ string.
36
+
37
+ .. doctest::
38
+
39
+ >>> from dateutil.tz import tzoffset
40
+ >>> from dateutil.parser import parse
41
+ >>> from dateutil.utils import default_tzinfo
42
+ >>> dflt_tz = tzoffset("EST", -18000)
43
+ >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz))
44
+ 2014-01-01 12:30:00+00:00
45
+ >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz))
46
+ 2014-01-01 12:30:00-05:00
47
+
48
+ :param dt:
49
+ The datetime on which to replace the time zone
50
+
51
+ :param tzinfo:
52
+ The :py:class:`datetime.tzinfo` subclass instance to assign to
53
+ ``dt`` if (and only if) it is naive.
54
+
55
+ :return:
56
+ Returns an aware :py:class:`datetime.datetime`.
57
+ """
58
+ if dt.tzinfo is not None:
59
+ return dt
60
+ else:
61
+ return dt.replace(tzinfo=tzinfo)
62
+
63
+
64
+ def within_delta(dt1, dt2, delta):
65
+ """
66
+ Useful for comparing two datetimes that may have a negligible difference
67
+ to be considered equal.
68
+ """
69
+ delta = abs(delta)
70
+ difference = dt1 - dt2
71
+ return -delta <= difference <= delta
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/__init__.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ try:
2
+ from importlib.metadata import entry_points
3
+ except ImportError: # python < 3.8
4
+ try:
5
+ from importlib_metadata import entry_points
6
+ except ImportError:
7
+ entry_points = None
8
+
9
+
10
+ from . import _version, caching
11
+ from .callbacks import Callback
12
+ from .compression import available_compressions
13
+ from .core import get_fs_token_paths, open, open_files, open_local
14
+ from .exceptions import FSTimeoutError
15
+ from .mapping import FSMap, get_mapper
16
+ from .registry import (
17
+ available_protocols,
18
+ filesystem,
19
+ get_filesystem_class,
20
+ register_implementation,
21
+ registry,
22
+ )
23
+ from .spec import AbstractFileSystem
24
+
25
+ __version__ = _version.get_versions()["version"]
26
+
27
+ __all__ = [
28
+ "AbstractFileSystem",
29
+ "FSTimeoutError",
30
+ "FSMap",
31
+ "filesystem",
32
+ "register_implementation",
33
+ "get_filesystem_class",
34
+ "get_fs_token_paths",
35
+ "get_mapper",
36
+ "open",
37
+ "open_files",
38
+ "open_local",
39
+ "registry",
40
+ "caching",
41
+ "Callback",
42
+ "available_protocols",
43
+ "available_compressions",
44
+ ]
45
+
46
+
47
+ def process_entries():
48
+ if entry_points is not None:
49
+ try:
50
+ eps = entry_points()
51
+ except TypeError:
52
+ pass # importlib-metadata < 0.8
53
+ else:
54
+ if hasattr(eps, "select"): # Python 3.10+ / importlib_metadata >= 3.9.0
55
+ specs = eps.select(group="fsspec.specs")
56
+ else:
57
+ specs = eps.get("fsspec.specs", [])
58
+ for spec in specs:
59
+ err_msg = f"Unable to load filesystem from {spec}"
60
+ register_implementation(
61
+ spec.name, spec.value.replace(":", "."), errtxt=err_msg
62
+ )
63
+
64
+
65
+ process_entries()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/_version.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # This file was generated by 'versioneer.py' (0.20) from
3
+ # revision-control system data, or from the parent directory name of an
4
+ # unpacked source archive. Distribution tarballs contain a pre-generated copy
5
+ # of this file.
6
+
7
+ import json
8
+
9
+ version_json = '''
10
+ {
11
+ "date": "2022-05-19T14:13:38-0400",
12
+ "dirty": false,
13
+ "error": null,
14
+ "full-revisionid": "148a6861481f824afb88c7c50955aa6ed4e25d32",
15
+ "version": "2022.5.0"
16
+ }
17
+ ''' # END VERSION_JSON
18
+
19
+
20
+ def get_versions():
21
+ return json.loads(version_json)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/archive.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fsspec import AbstractFileSystem
2
+ from fsspec.utils import tokenize
3
+
4
+
5
+ class AbstractArchiveFileSystem(AbstractFileSystem):
6
+ """
7
+ A generic superclass for implementing Archive-based filesystems.
8
+
9
+ Currently, it is shared amongst `ZipFileSystem`, `LibArchiveFileSystem` and
10
+ `TarFileSystem`.
11
+ """
12
+
13
+ def __str__(self):
14
+ return "<Archive-like object %s at %s>" % (type(self).__name__, id(self))
15
+
16
+ __repr__ = __str__
17
+
18
+ def ukey(self, path):
19
+ return tokenize(path, self.fo, self.protocol)
20
+
21
+ def _all_dirnames(self, paths):
22
+ """Returns *all* directory names for each path in paths, including intermediate ones.
23
+
24
+ Parameters
25
+ ----------
26
+ paths: Iterable of path strings
27
+ """
28
+ if len(paths) == 0:
29
+ return set()
30
+
31
+ dirnames = {self._parent(path) for path in paths} - {self.root_marker}
32
+ return dirnames | self._all_dirnames(dirnames)
33
+
34
+ def info(self, path, **kwargs):
35
+ self._get_dirs()
36
+ path = self._strip_protocol(path)
37
+ if path in self.dir_cache:
38
+ return self.dir_cache[path]
39
+ elif path + "/" in self.dir_cache:
40
+ return self.dir_cache[path + "/"]
41
+ else:
42
+ raise FileNotFoundError(path)
43
+
44
+ def ls(self, path, detail=False, **kwargs):
45
+ self._get_dirs()
46
+ paths = {}
47
+ for p, f in self.dir_cache.items():
48
+ p = p.rstrip("/")
49
+ if "/" in p:
50
+ root = p.rsplit("/", 1)[0]
51
+ else:
52
+ root = ""
53
+ if root == path.rstrip("/"):
54
+ paths[p] = f
55
+ elif all(
56
+ (a == b)
57
+ for a, b in zip(path.split("/"), [""] + p.strip("/").split("/"))
58
+ ):
59
+ # root directory entry
60
+ ppath = p.rstrip("/").split("/", 1)[0]
61
+ if ppath not in paths:
62
+ out = {"name": ppath + "/", "size": 0, "type": "directory"}
63
+ paths[ppath] = out
64
+ out = list(paths.values())
65
+ if detail:
66
+ return out
67
+ else:
68
+ return list(sorted(f["name"] for f in out))
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/asyn.py ADDED
@@ -0,0 +1,933 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import asyncio.events
3
+ import functools
4
+ import inspect
5
+ import io
6
+ import os
7
+ import re
8
+ import sys
9
+ import threading
10
+ from contextlib import contextmanager
11
+ from glob import has_magic
12
+
13
+ from .callbacks import _DEFAULT_CALLBACK
14
+ from .exceptions import FSTimeoutError
15
+ from .spec import AbstractBufferedFile, AbstractFileSystem
16
+ from .utils import is_exception, other_paths
17
+
18
+ private = re.compile("_[^_]")
19
+
20
+
21
+ async def _runner(event, coro, result, timeout=None):
22
+ timeout = timeout if timeout else None # convert 0 or 0.0 to None
23
+ if timeout is not None:
24
+ coro = asyncio.wait_for(coro, timeout=timeout)
25
+ try:
26
+ result[0] = await coro
27
+ except Exception as ex:
28
+ result[0] = ex
29
+ finally:
30
+ event.set()
31
+
32
+
33
+ def sync(loop, func, *args, timeout=None, **kwargs):
34
+ """
35
+ Make loop run coroutine until it returns. Runs in other thread
36
+ """
37
+ timeout = timeout if timeout else None # convert 0 or 0.0 to None
38
+ # NB: if the loop is not running *yet*, it is OK to submit work
39
+ # and we will wait for it
40
+ if loop is None or loop.is_closed():
41
+ raise RuntimeError("Loop is not running")
42
+ try:
43
+ loop0 = asyncio.events.get_running_loop()
44
+ if loop0 is loop:
45
+ raise NotImplementedError("Calling sync() from within a running loop")
46
+ except RuntimeError:
47
+ pass
48
+ coro = func(*args, **kwargs)
49
+ result = [None]
50
+ event = threading.Event()
51
+ asyncio.run_coroutine_threadsafe(_runner(event, coro, result, timeout), loop)
52
+ while True:
53
+ # this loops allows thread to get interrupted
54
+ if event.wait(1):
55
+ break
56
+ if timeout is not None:
57
+ timeout -= 1
58
+ if timeout < 0:
59
+ raise FSTimeoutError
60
+
61
+ return_result = result[0]
62
+ if isinstance(return_result, asyncio.TimeoutError):
63
+ # suppress asyncio.TimeoutError, raise FSTimeoutError
64
+ raise FSTimeoutError from return_result
65
+ elif isinstance(return_result, BaseException):
66
+ raise return_result
67
+ else:
68
+ return return_result
69
+
70
+
71
+ iothread = [None] # dedicated fsspec IO thread
72
+ loop = [None] # global event loop for any non-async instance
73
+ lock = threading.Lock() # for setting exactly one thread
74
+
75
+
76
+ def sync_wrapper(func, obj=None):
77
+ """Given a function, make so can be called in async or blocking contexts
78
+
79
+ Leave obj=None if defining within a class. Pass the instance if attaching
80
+ as an attribute of the instance.
81
+ """
82
+
83
+ @functools.wraps(func)
84
+ def wrapper(*args, **kwargs):
85
+ self = obj or args[0]
86
+ return sync(self.loop, func, *args, **kwargs)
87
+
88
+ return wrapper
89
+
90
+
91
+ @contextmanager
92
+ def _selector_policy():
93
+ original_policy = asyncio.get_event_loop_policy()
94
+ try:
95
+ if (
96
+ sys.version_info >= (3, 8)
97
+ and os.name == "nt"
98
+ and hasattr(asyncio, "WindowsSelectorEventLoopPolicy")
99
+ ):
100
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
101
+
102
+ yield
103
+ finally:
104
+ asyncio.set_event_loop_policy(original_policy)
105
+
106
+
107
+ def get_running_loop():
108
+ if hasattr(asyncio, "get_running_loop"):
109
+ return asyncio.get_running_loop()
110
+ else:
111
+ loop = asyncio._get_running_loop()
112
+ if loop is None:
113
+ raise RuntimeError("no running event loop")
114
+ else:
115
+ return loop
116
+
117
+
118
+ def get_loop():
119
+ """Create or return the default fsspec IO loop
120
+
121
+ The loop will be running on a separate thread.
122
+ """
123
+ if loop[0] is None:
124
+ with lock:
125
+ # repeat the check just in case the loop got filled between the
126
+ # previous two calls from another thread
127
+ if loop[0] is None:
128
+ with _selector_policy():
129
+ loop[0] = asyncio.new_event_loop()
130
+ th = threading.Thread(target=loop[0].run_forever, name="fsspecIO")
131
+ th.daemon = True
132
+ th.start()
133
+ iothread[0] = th
134
+ return loop[0]
135
+
136
+
137
+ @contextmanager
138
+ def fsspec_loop():
139
+ """Temporarily switch the current event loop to the fsspec's
140
+ own loop, and then revert it back after the context gets
141
+ terminated.
142
+ """
143
+ try:
144
+ original_loop = get_running_loop()
145
+ except RuntimeError:
146
+ original_loop = None
147
+
148
+ fsspec_loop = get_loop()
149
+ try:
150
+ asyncio._set_running_loop(fsspec_loop)
151
+ yield fsspec_loop
152
+ finally:
153
+ asyncio._set_running_loop(original_loop)
154
+
155
+
156
+ try:
157
+ import resource
158
+ except ImportError:
159
+ resource = None
160
+ ResourceError = OSError
161
+ else:
162
+ ResourceEror = resource.error
163
+
164
+ _DEFAULT_BATCH_SIZE = 128
165
+ _NOFILES_DEFAULT_BATCH_SIZE = 1280
166
+
167
+
168
+ def _get_batch_size(nofiles=False):
169
+ from fsspec.config import conf
170
+
171
+ if nofiles:
172
+ if "nofiles_gather_batch_size" in conf:
173
+ return conf["nofiles_gather_batch_size"]
174
+ else:
175
+ if "gather_batch_size" in conf:
176
+ return conf["gather_batch_size"]
177
+ if nofiles:
178
+ return _NOFILES_DEFAULT_BATCH_SIZE
179
+ if resource is None:
180
+ return _DEFAULT_BATCH_SIZE
181
+
182
+ try:
183
+ soft_limit, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
184
+ except (ImportError, ValueError, ResourceError):
185
+ return _DEFAULT_BATCH_SIZE
186
+
187
+ if soft_limit == resource.RLIM_INFINITY:
188
+ return -1
189
+ else:
190
+ return soft_limit // 8
191
+
192
+
193
+ async def _run_coros_in_chunks(
194
+ coros,
195
+ batch_size=None,
196
+ callback=_DEFAULT_CALLBACK,
197
+ timeout=None,
198
+ return_exceptions=False,
199
+ nofiles=False,
200
+ ):
201
+ """Run the given coroutines in chunks.
202
+
203
+ Parameters
204
+ ----------
205
+ coros: list of coroutines to run
206
+ batch_size: int or None
207
+ Number of coroutines to submit/wait on simultaneously.
208
+ If -1, then it will not be any throttling. If
209
+ None, it will be inferred from _get_batch_size()
210
+ callback: fsspec.callbacks.Callback instance
211
+ Gets a relative_update when each coroutine completes
212
+ timeout: number or None
213
+ If given, each coroutine times out after this time. Note that, since
214
+ there are multiple batches, the total run time of this function will in
215
+ general be longer
216
+ return_exceptions: bool
217
+ Same meaning as in asyncio.gather
218
+ nofiles: bool
219
+ If inferring the batch_size, does this operation involve local files?
220
+ If yes, you normally expect smaller batches.
221
+ """
222
+
223
+ if batch_size is None:
224
+ batch_size = _get_batch_size(nofiles=nofiles)
225
+
226
+ if batch_size == -1:
227
+ batch_size = len(coros)
228
+
229
+ assert batch_size > 0
230
+ results = []
231
+ for start in range(0, len(coros), batch_size):
232
+ chunk = [
233
+ asyncio.Task(asyncio.wait_for(c, timeout=timeout))
234
+ for c in coros[start : start + batch_size]
235
+ ]
236
+ if callback is not _DEFAULT_CALLBACK:
237
+ [
238
+ t.add_done_callback(lambda *_, **__: callback.relative_update(1))
239
+ for t in chunk
240
+ ]
241
+ results.extend(
242
+ await asyncio.gather(*chunk, return_exceptions=return_exceptions),
243
+ )
244
+ return results
245
+
246
+
247
+ # these methods should be implemented as async by any async-able backend
248
+ async_methods = [
249
+ "_ls",
250
+ "_cat_file",
251
+ "_get_file",
252
+ "_put_file",
253
+ "_rm_file",
254
+ "_cp_file",
255
+ "_pipe_file",
256
+ "_expand_path",
257
+ "_info",
258
+ "_isfile",
259
+ "_isdir",
260
+ "_exists",
261
+ "_walk",
262
+ "_glob",
263
+ "_find",
264
+ "_du",
265
+ "_size",
266
+ "_mkdir",
267
+ "_makedirs",
268
+ ]
269
+
270
+
271
+ class AsyncFileSystem(AbstractFileSystem):
272
+ """Async file operations, default implementations
273
+
274
+ Passes bulk operations to asyncio.gather for concurrent operation.
275
+
276
+ Implementations that have concurrent batch operations and/or async methods
277
+ should inherit from this class instead of AbstractFileSystem. Docstrings are
278
+ copied from the un-underscored method in AbstractFileSystem, if not given.
279
+ """
280
+
281
+ # note that methods do not have docstring here; they will be copied
282
+ # for _* methods and inferred for overridden methods.
283
+
284
+ async_impl = True
285
+ disable_throttling = False
286
+
287
+ def __init__(self, *args, asynchronous=False, loop=None, batch_size=None, **kwargs):
288
+ self.asynchronous = asynchronous
289
+ self._pid = os.getpid()
290
+ if not asynchronous:
291
+ self._loop = loop or get_loop()
292
+ else:
293
+ self._loop = None
294
+ self.batch_size = batch_size
295
+ super().__init__(*args, **kwargs)
296
+
297
+ @property
298
+ def loop(self):
299
+ if self._pid != os.getpid():
300
+ raise RuntimeError("This class is not fork-safe")
301
+ return self._loop
302
+
303
+ async def _rm_file(self, path, **kwargs):
304
+ raise NotImplementedError
305
+
306
+ async def _rm(self, path, recursive=False, batch_size=None, **kwargs):
307
+ # TODO: implement on_error
308
+ batch_size = batch_size or self.batch_size
309
+ path = await self._expand_path(path, recursive=recursive)
310
+ return await _run_coros_in_chunks(
311
+ [self._rm_file(p, **kwargs) for p in path],
312
+ batch_size=batch_size,
313
+ nofiles=True,
314
+ )
315
+
316
+ async def _cp_file(self, path1, path2, **kwargs):
317
+ raise NotImplementedError
318
+
319
+ async def _copy(
320
+ self,
321
+ path1,
322
+ path2,
323
+ recursive=False,
324
+ on_error=None,
325
+ maxdepth=None,
326
+ batch_size=None,
327
+ **kwargs,
328
+ ):
329
+ if on_error is None and recursive:
330
+ on_error = "ignore"
331
+ elif on_error is None:
332
+ on_error = "raise"
333
+
334
+ paths = await self._expand_path(path1, maxdepth=maxdepth, recursive=recursive)
335
+ path2 = other_paths(paths, path2)
336
+ batch_size = batch_size or self.batch_size
337
+ coros = [self._cp_file(p1, p2, **kwargs) for p1, p2 in zip(paths, path2)]
338
+ result = await _run_coros_in_chunks(
339
+ coros, batch_size=batch_size, return_exceptions=True, nofiles=True
340
+ )
341
+
342
+ for ex in filter(is_exception, result):
343
+ if on_error == "ignore" and isinstance(ex, FileNotFoundError):
344
+ continue
345
+ raise ex
346
+
347
+ async def _pipe(self, path, value=None, batch_size=None, **kwargs):
348
+ if isinstance(path, str):
349
+ path = {path: value}
350
+ batch_size = batch_size or self.batch_size
351
+ return await _run_coros_in_chunks(
352
+ [self._pipe_file(k, v, **kwargs) for k, v in path.items()],
353
+ batch_size=batch_size,
354
+ nofiles=True,
355
+ )
356
+
357
+ async def _process_limits(self, url, start, end):
358
+ """Helper for "Range"-based _cat_file"""
359
+ size = None
360
+ suff = False
361
+ if start is not None and start < 0:
362
+ # if start is negative and end None, end is the "suffix length"
363
+ if end is None:
364
+ end = -start
365
+ start = ""
366
+ suff = True
367
+ else:
368
+ size = size or (await self._info(url))["size"]
369
+ start = size + start
370
+ elif start is None:
371
+ start = 0
372
+ if not suff:
373
+ if end is not None and end < 0:
374
+ if start is not None:
375
+ size = size or (await self._info(url))["size"]
376
+ end = size + end
377
+ elif end is None:
378
+ end = ""
379
+ if isinstance(end, int):
380
+ end -= 1 # bytes range is inclusive
381
+ return "bytes=%s-%s" % (start, end)
382
+
383
+ async def _cat_file(self, path, start=None, end=None, **kwargs):
384
+ raise NotImplementedError
385
+
386
+ async def _cat(
387
+ self, path, recursive=False, on_error="raise", batch_size=None, **kwargs
388
+ ):
389
+ paths = await self._expand_path(path, recursive=recursive)
390
+ coros = [self._cat_file(path, **kwargs) for path in paths]
391
+ batch_size = batch_size or self.batch_size
392
+ out = await _run_coros_in_chunks(
393
+ coros, batch_size=batch_size, nofiles=True, return_exceptions=True
394
+ )
395
+ if on_error == "raise":
396
+ ex = next(filter(is_exception, out), False)
397
+ if ex:
398
+ raise ex
399
+ if (
400
+ len(paths) > 1
401
+ or isinstance(path, list)
402
+ or paths[0] != self._strip_protocol(path)
403
+ ):
404
+ return {
405
+ k: v
406
+ for k, v in zip(paths, out)
407
+ if on_error != "omit" or not is_exception(v)
408
+ }
409
+ else:
410
+ return out[0]
411
+
412
+ async def _cat_ranges(
413
+ self, paths, starts, ends, max_gap=None, batch_size=None, **kwargs
414
+ ):
415
+ # TODO: on_error
416
+ if max_gap is not None:
417
+ # use utils.merge_offset_ranges
418
+ raise NotImplementedError
419
+ if not isinstance(paths, list):
420
+ raise TypeError
421
+ if not isinstance(starts, list):
422
+ starts = [starts] * len(paths)
423
+ if not isinstance(ends, list):
424
+ ends = [starts] * len(paths)
425
+ if len(starts) != len(paths) or len(ends) != len(paths):
426
+ raise ValueError
427
+ coros = [
428
+ self._cat_file(p, start=s, end=e, **kwargs)
429
+ for p, s, e in zip(paths, starts, ends)
430
+ ]
431
+ batch_size = batch_size or self.batch_size
432
+ return await _run_coros_in_chunks(coros, batch_size=batch_size, nofiles=True)
433
+
434
+ async def _put_file(self, lpath, rpath, **kwargs):
435
+ raise NotImplementedError
436
+
437
+ async def _put(
438
+ self,
439
+ lpath,
440
+ rpath,
441
+ recursive=False,
442
+ callback=_DEFAULT_CALLBACK,
443
+ batch_size=None,
444
+ **kwargs,
445
+ ):
446
+ """Copy file(s) from local.
447
+
448
+ Copies a specific file or tree of files (if recursive=True). If rpath
449
+ ends with a "/", it will be assumed to be a directory, and target files
450
+ will go within.
451
+
452
+ The put_file method will be called concurrently on a batch of files. The
453
+ batch_size option can configure the amount of futures that can be executed
454
+ at the same time. If it is -1, then all the files will be uploaded concurrently.
455
+ The default can be set for this instance by passing "batch_size" in the
456
+ constructor, or for all instances by setting the "gather_batch_size" key
457
+ in ``fsspec.config.conf``, falling back to 1/8th of the system limit .
458
+ """
459
+ from .implementations.local import LocalFileSystem, make_path_posix
460
+
461
+ rpath = self._strip_protocol(rpath)
462
+ if isinstance(lpath, str):
463
+ lpath = make_path_posix(lpath)
464
+ fs = LocalFileSystem()
465
+ lpaths = fs.expand_path(lpath, recursive=recursive)
466
+ rpaths = other_paths(
467
+ lpaths, rpath, exists=isinstance(rpath, str) and await self._isdir(rpath)
468
+ )
469
+
470
+ is_dir = {l: os.path.isdir(l) for l in lpaths}
471
+ rdirs = [r for l, r in zip(lpaths, rpaths) if is_dir[l]]
472
+ file_pairs = [(l, r) for l, r in zip(lpaths, rpaths) if not is_dir[l]]
473
+
474
+ await asyncio.gather(*[self._makedirs(d, exist_ok=True) for d in rdirs])
475
+ batch_size = batch_size or self.batch_size
476
+
477
+ coros = []
478
+ callback.set_size(len(file_pairs))
479
+ for lfile, rfile in file_pairs:
480
+ callback.branch(lfile, rfile, kwargs)
481
+ coros.append(self._put_file(lfile, rfile, **kwargs))
482
+
483
+ return await _run_coros_in_chunks(
484
+ coros, batch_size=batch_size, callback=callback
485
+ )
486
+
487
+ async def _get_file(self, rpath, lpath, **kwargs):
488
+ raise NotImplementedError
489
+
490
+ async def _get(
491
+ self, rpath, lpath, recursive=False, callback=_DEFAULT_CALLBACK, **kwargs
492
+ ):
493
+ """Copy file(s) to local.
494
+
495
+ Copies a specific file or tree of files (if recursive=True). If lpath
496
+ ends with a "/", it will be assumed to be a directory, and target files
497
+ will go within. Can submit a list of paths, which may be glob-patterns
498
+ and will be expanded.
499
+
500
+ The get_file method will be called concurrently on a batch of files. The
501
+ batch_size option can configure the amount of futures that can be executed
502
+ at the same time. If it is -1, then all the files will be uploaded concurrently.
503
+ The default can be set for this instance by passing "batch_size" in the
504
+ constructor, or for all instances by setting the "gather_batch_size" key
505
+ in ``fsspec.config.conf``, falling back to 1/8th of the system limit .
506
+ """
507
+ from fsspec.implementations.local import make_path_posix
508
+
509
+ rpath = self._strip_protocol(rpath)
510
+ lpath = make_path_posix(lpath)
511
+ rpaths = await self._expand_path(rpath, recursive=recursive)
512
+ lpaths = other_paths(rpaths, lpath)
513
+ [os.makedirs(os.path.dirname(lp), exist_ok=True) for lp in lpaths]
514
+ batch_size = kwargs.pop("batch_size", self.batch_size)
515
+
516
+ coros = []
517
+ callback.set_size(len(lpaths))
518
+ for lpath, rpath in zip(lpaths, rpaths):
519
+ callback.branch(rpath, lpath, kwargs)
520
+ coros.append(self._get_file(rpath, lpath, **kwargs))
521
+ return await _run_coros_in_chunks(
522
+ coros, batch_size=batch_size, callback=callback
523
+ )
524
+
525
+ async def _isfile(self, path):
526
+ try:
527
+ return (await self._info(path))["type"] == "file"
528
+ except: # noqa: E722
529
+ return False
530
+
531
+ async def _isdir(self, path):
532
+ try:
533
+ return (await self._info(path))["type"] == "directory"
534
+ except IOError:
535
+ return False
536
+
537
+ async def _size(self, path):
538
+ return (await self._info(path)).get("size", None)
539
+
540
+ async def _sizes(self, paths, batch_size=None):
541
+ batch_size = batch_size or self.batch_size
542
+ return await _run_coros_in_chunks(
543
+ [self._size(p) for p in paths], batch_size=batch_size
544
+ )
545
+
546
+ async def _exists(self, path):
547
+ try:
548
+ await self._info(path)
549
+ return True
550
+ except FileNotFoundError:
551
+ return False
552
+
553
+ async def _info(self, path, **kwargs):
554
+ raise NotImplementedError
555
+
556
+ async def _ls(self, path, detail=True, **kwargs):
557
+ raise NotImplementedError
558
+
559
+ async def _walk(self, path, maxdepth=None, **kwargs):
560
+ path = self._strip_protocol(path)
561
+ full_dirs = {}
562
+ dirs = {}
563
+ files = {}
564
+
565
+ detail = kwargs.pop("detail", False)
566
+ try:
567
+ listing = await self._ls(path, detail=True, **kwargs)
568
+ except (FileNotFoundError, IOError):
569
+ if detail:
570
+ yield path, {}, {}
571
+ else:
572
+ yield path, [], []
573
+ return
574
+
575
+ for info in listing:
576
+ # each info name must be at least [path]/part , but here
577
+ # we check also for names like [path]/part/
578
+ pathname = info["name"].rstrip("/")
579
+ name = pathname.rsplit("/", 1)[-1]
580
+ if info["type"] == "directory" and pathname != path:
581
+ # do not include "self" path
582
+ full_dirs[pathname] = info
583
+ dirs[name] = info
584
+ elif pathname == path:
585
+ # file-like with same name as give path
586
+ files[""] = info
587
+ else:
588
+ files[name] = info
589
+
590
+ if detail:
591
+ yield path, dirs, files
592
+ else:
593
+ yield path, list(dirs), list(files)
594
+
595
+ if maxdepth is not None:
596
+ maxdepth -= 1
597
+ if maxdepth < 1:
598
+ return
599
+
600
+ for d in full_dirs:
601
+ async for _ in self._walk(d, maxdepth=maxdepth, detail=detail, **kwargs):
602
+ yield _
603
+
604
+ async def _glob(self, path, **kwargs):
605
+ import re
606
+
607
+ ends = path.endswith("/")
608
+ path = self._strip_protocol(path)
609
+ indstar = path.find("*") if path.find("*") >= 0 else len(path)
610
+ indques = path.find("?") if path.find("?") >= 0 else len(path)
611
+ indbrace = path.find("[") if path.find("[") >= 0 else len(path)
612
+
613
+ ind = min(indstar, indques, indbrace)
614
+
615
+ detail = kwargs.pop("detail", False)
616
+
617
+ if not has_magic(path):
618
+ root = path
619
+ depth = 1
620
+ if ends:
621
+ path += "/*"
622
+ elif await self._exists(path):
623
+ if not detail:
624
+ return [path]
625
+ else:
626
+ return {path: await self._info(path)}
627
+ else:
628
+ if not detail:
629
+ return [] # glob of non-existent returns empty
630
+ else:
631
+ return {}
632
+ elif "/" in path[:ind]:
633
+ ind2 = path[:ind].rindex("/")
634
+ root = path[: ind2 + 1]
635
+ depth = None if "**" in path else path[ind2 + 1 :].count("/") + 1
636
+ else:
637
+ root = ""
638
+ depth = None if "**" in path else path[ind + 1 :].count("/") + 1
639
+
640
+ allpaths = await self._find(
641
+ root, maxdepth=depth, withdirs=True, detail=True, **kwargs
642
+ )
643
+ # Escape characters special to python regex, leaving our supported
644
+ # special characters in place.
645
+ # See https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html
646
+ # for shell globbing details.
647
+ pattern = (
648
+ "^"
649
+ + (
650
+ path.replace("\\", r"\\")
651
+ .replace(".", r"\.")
652
+ .replace("+", r"\+")
653
+ .replace("//", "/")
654
+ .replace("(", r"\(")
655
+ .replace(")", r"\)")
656
+ .replace("|", r"\|")
657
+ .replace("^", r"\^")
658
+ .replace("$", r"\$")
659
+ .replace("{", r"\{")
660
+ .replace("}", r"\}")
661
+ .rstrip("/")
662
+ .replace("?", ".")
663
+ )
664
+ + "$"
665
+ )
666
+ pattern = re.sub("[*]{2}", "=PLACEHOLDER=", pattern)
667
+ pattern = re.sub("[*]", "[^/]*", pattern)
668
+ pattern = re.compile(pattern.replace("=PLACEHOLDER=", ".*"))
669
+ out = {
670
+ p: allpaths[p]
671
+ for p in sorted(allpaths)
672
+ if pattern.match(p.replace("//", "/").rstrip("/"))
673
+ }
674
+ if detail:
675
+ return out
676
+ else:
677
+ return list(out)
678
+
679
+ async def _du(self, path, total=True, maxdepth=None, **kwargs):
680
+ sizes = {}
681
+ # async for?
682
+ for f in await self._find(path, maxdepth=maxdepth, **kwargs):
683
+ info = await self._info(f)
684
+ sizes[info["name"]] = info["size"]
685
+ if total:
686
+ return sum(sizes.values())
687
+ else:
688
+ return sizes
689
+
690
+ async def _find(self, path, maxdepth=None, withdirs=False, **kwargs):
691
+ path = self._strip_protocol(path)
692
+ out = dict()
693
+ detail = kwargs.pop("detail", False)
694
+ # async for?
695
+ async for _, dirs, files in self._walk(path, maxdepth, detail=True, **kwargs):
696
+ if withdirs:
697
+ files.update(dirs)
698
+ out.update({info["name"]: info for name, info in files.items()})
699
+ if not out and (await self._isfile(path)):
700
+ # walk works on directories, but find should also return [path]
701
+ # when path happens to be a file
702
+ out[path] = {}
703
+ names = sorted(out)
704
+ if not detail:
705
+ return names
706
+ else:
707
+ return {name: out[name] for name in names}
708
+
709
+ async def _expand_path(self, path, recursive=False, maxdepth=None):
710
+ if isinstance(path, str):
711
+ out = await self._expand_path([path], recursive, maxdepth)
712
+ else:
713
+ # reduce depth on each recursion level unless None or 0
714
+ maxdepth = maxdepth if not maxdepth else maxdepth - 1
715
+ out = set()
716
+ path = [self._strip_protocol(p) for p in path]
717
+ for p in path: # can gather here
718
+ if has_magic(p):
719
+ bit = set(await self._glob(p))
720
+ out |= bit
721
+ if recursive:
722
+ out |= set(
723
+ await self._expand_path(
724
+ list(bit), recursive=recursive, maxdepth=maxdepth
725
+ )
726
+ )
727
+ continue
728
+ elif recursive:
729
+ rec = set(await self._find(p, maxdepth=maxdepth, withdirs=True))
730
+ out |= rec
731
+ if p not in out and (recursive is False or (await self._exists(p))):
732
+ # should only check once, for the root
733
+ out.add(p)
734
+ if not out:
735
+ raise FileNotFoundError(path)
736
+ return list(sorted(out))
737
+
738
+ async def _mkdir(self, path, create_parents=True, **kwargs):
739
+ pass # not necessary to implement, may not have directories
740
+
741
+ async def _makedirs(self, path, exist_ok=False):
742
+ pass # not necessary to implement, may not have directories
743
+
744
+ async def open_async(self, path, mode="rb", **kwargs):
745
+ if "b" not in mode or kwargs.get("compression"):
746
+ raise ValueError
747
+ raise NotImplementedError
748
+
749
+
750
+ def mirror_sync_methods(obj):
751
+ """Populate sync and async methods for obj
752
+
753
+ For each method will create a sync version if the name refers to an async method
754
+ (coroutine) and there is no override in the child class; will create an async
755
+ method for the corresponding sync method if there is no implementation.
756
+
757
+ Uses the methods specified in
758
+ - async_methods: the set that an implementation is expected to provide
759
+ - default_async_methods: that can be derived from their sync version in
760
+ AbstractFileSystem
761
+ - AsyncFileSystem: async-specific default coroutines
762
+ """
763
+ from fsspec import AbstractFileSystem
764
+
765
+ for method in async_methods + dir(AsyncFileSystem):
766
+ if not method.startswith("_"):
767
+ continue
768
+ smethod = method[1:]
769
+ if private.match(method):
770
+ isco = inspect.iscoroutinefunction(getattr(obj, method, None))
771
+ unsync = getattr(getattr(obj, smethod, False), "__func__", None)
772
+ is_default = unsync is getattr(AbstractFileSystem, smethod, "")
773
+ if isco and is_default:
774
+ mth = sync_wrapper(getattr(obj, method), obj=obj)
775
+ setattr(obj, smethod, mth)
776
+ if not mth.__doc__:
777
+ mth.__doc__ = getattr(
778
+ getattr(AbstractFileSystem, smethod, None), "__doc__", ""
779
+ )
780
+
781
+
782
+ class FSSpecCoroutineCancel(Exception):
783
+ pass
784
+
785
+
786
+ def _dump_running_tasks(
787
+ printout=True, cancel=True, exc=FSSpecCoroutineCancel, with_task=False
788
+ ):
789
+ import traceback
790
+
791
+ tasks = [t for t in asyncio.tasks.all_tasks(loop[0]) if not t.done()]
792
+ if printout:
793
+ [task.print_stack() for task in tasks]
794
+ out = [
795
+ {
796
+ "locals": task._coro.cr_frame.f_locals,
797
+ "file": task._coro.cr_frame.f_code.co_filename,
798
+ "firstline": task._coro.cr_frame.f_code.co_firstlineno,
799
+ "linelo": task._coro.cr_frame.f_lineno,
800
+ "stack": traceback.format_stack(task._coro.cr_frame),
801
+ "task": task if with_task else None,
802
+ }
803
+ for task in tasks
804
+ ]
805
+ if cancel:
806
+ for t in tasks:
807
+ cbs = t._callbacks
808
+ t.cancel()
809
+ asyncio.futures.Future.set_exception(t, exc)
810
+ asyncio.futures.Future.cancel(t)
811
+ [cb[0](t) for cb in cbs] # cancels any dependent concurrent.futures
812
+ try:
813
+ t._coro.throw(exc) # exits coro, unless explicitly handled
814
+ except exc:
815
+ pass
816
+ return out
817
+
818
+
819
+ class AbstractAsyncStreamedFile(AbstractBufferedFile):
820
+ # no read buffering, and always auto-commit
821
+ # TODO: readahead might still be useful here, but needs async version
822
+
823
+ async def read(self, length=-1):
824
+ """
825
+ Return data from cache, or fetch pieces as necessary
826
+
827
+ Parameters
828
+ ----------
829
+ length: int (-1)
830
+ Number of bytes to read; if <0, all remaining bytes.
831
+ """
832
+ length = -1 if length is None else int(length)
833
+ if self.mode != "rb":
834
+ raise ValueError("File not in read mode")
835
+ if length < 0:
836
+ length = self.size - self.loc
837
+ if self.closed:
838
+ raise ValueError("I/O operation on closed file.")
839
+ if length == 0:
840
+ # don't even bother calling fetch
841
+ return b""
842
+ out = await self._fetch_range(self.loc, self.loc + length)
843
+ self.loc += len(out)
844
+ return out
845
+
846
+ async def write(self, data):
847
+ """
848
+ Write data to buffer.
849
+
850
+ Buffer only sent on flush() or if buffer is greater than
851
+ or equal to blocksize.
852
+
853
+ Parameters
854
+ ----------
855
+ data: bytes
856
+ Set of bytes to be written.
857
+ """
858
+ if self.mode not in {"wb", "ab"}:
859
+ raise ValueError("File not in write mode")
860
+ if self.closed:
861
+ raise ValueError("I/O operation on closed file.")
862
+ if self.forced:
863
+ raise ValueError("This file has been force-flushed, can only close")
864
+ out = self.buffer.write(data)
865
+ self.loc += out
866
+ if self.buffer.tell() >= self.blocksize:
867
+ await self.flush()
868
+ return out
869
+
870
+ async def close(self):
871
+ """Close file
872
+
873
+ Finalizes writes, discards cache
874
+ """
875
+ if getattr(self, "_unclosable", False):
876
+ return
877
+ if self.closed:
878
+ return
879
+ if self.mode == "rb":
880
+ self.cache = None
881
+ else:
882
+ if not self.forced:
883
+ await self.flush(force=True)
884
+
885
+ if self.fs is not None:
886
+ self.fs.invalidate_cache(self.path)
887
+ self.fs.invalidate_cache(self.fs._parent(self.path))
888
+
889
+ self.closed = True
890
+
891
+ async def flush(self, force=False):
892
+ if self.closed:
893
+ raise ValueError("Flush on closed file")
894
+ if force and self.forced:
895
+ raise ValueError("Force flush cannot be called more than once")
896
+ if force:
897
+ self.forced = True
898
+
899
+ if self.mode not in {"wb", "ab"}:
900
+ # no-op to flush on read-mode
901
+ return
902
+
903
+ if not force and self.buffer.tell() < self.blocksize:
904
+ # Defer write on small block
905
+ return
906
+
907
+ if self.offset is None:
908
+ # Initialize a multipart upload
909
+ self.offset = 0
910
+ try:
911
+ await self._initiate_upload()
912
+ except: # noqa: E722
913
+ self.closed = True
914
+ raise
915
+
916
+ if self._upload_chunk(final=force) is not False:
917
+ self.offset += self.buffer.seek(0, 2)
918
+ self.buffer = io.BytesIO()
919
+
920
+ async def __aenter__(self):
921
+ return self
922
+
923
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
924
+ await self.close()
925
+
926
+ async def _fetch_range(self, start, end):
927
+ raise NotImplementedError
928
+
929
+ async def _initiate_upload(self):
930
+ raise NotImplementedError
931
+
932
+ async def _upload_chunk(self, final=False):
933
+ raise NotImplementedError
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/caching.py ADDED
@@ -0,0 +1,521 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+ import io
3
+ import logging
4
+ import math
5
+ import os
6
+ import warnings
7
+
8
+ logger = logging.getLogger("fsspec")
9
+
10
+
11
+ class BaseCache(object):
12
+ """Pass-though cache: doesn't keep anything, calls every time
13
+
14
+ Acts as base class for other cachers
15
+
16
+ Parameters
17
+ ----------
18
+ blocksize: int
19
+ How far to read ahead in numbers of bytes
20
+ fetcher: func
21
+ Function of the form f(start, end) which gets bytes from remote as
22
+ specified
23
+ size: int
24
+ How big this file is
25
+ """
26
+
27
+ name = "none"
28
+
29
+ def __init__(self, blocksize, fetcher, size):
30
+ self.blocksize = blocksize
31
+ self.fetcher = fetcher
32
+ self.size = size
33
+
34
+ def _fetch(self, start, stop):
35
+ if start is None:
36
+ start = 0
37
+ if stop is None:
38
+ stop = self.size
39
+ if start >= self.size or start >= stop:
40
+ return b""
41
+ return self.fetcher(start, stop)
42
+
43
+
44
+ class MMapCache(BaseCache):
45
+ """memory-mapped sparse file cache
46
+
47
+ Opens temporary file, which is filled blocks-wise when data is requested.
48
+ Ensure there is enough disc space in the temporary location.
49
+
50
+ This cache method might only work on posix
51
+ """
52
+
53
+ name = "mmap"
54
+
55
+ def __init__(self, blocksize, fetcher, size, location=None, blocks=None):
56
+ super().__init__(blocksize, fetcher, size)
57
+ self.blocks = set() if blocks is None else blocks
58
+ self.location = location
59
+ self.cache = self._makefile()
60
+
61
+ def _makefile(self):
62
+ import mmap
63
+ import tempfile
64
+
65
+ if self.size == 0:
66
+ return bytearray()
67
+
68
+ # posix version
69
+ if self.location is None or not os.path.exists(self.location):
70
+ if self.location is None:
71
+ fd = tempfile.TemporaryFile()
72
+ self.blocks = set()
73
+ else:
74
+ fd = io.open(self.location, "wb+")
75
+ fd.seek(self.size - 1)
76
+ fd.write(b"1")
77
+ fd.flush()
78
+ else:
79
+ fd = io.open(self.location, "rb+")
80
+
81
+ return mmap.mmap(fd.fileno(), self.size)
82
+
83
+ def _fetch(self, start, end):
84
+ logger.debug(f"MMap cache fetching {start}-{end}")
85
+ if start is None:
86
+ start = 0
87
+ if end is None:
88
+ end = self.size
89
+ if start >= self.size or start >= end:
90
+ return b""
91
+ start_block = start // self.blocksize
92
+ end_block = end // self.blocksize
93
+ need = [i for i in range(start_block, end_block + 1) if i not in self.blocks]
94
+ while need:
95
+ # TODO: not a for loop so we can consolidate blocks later to
96
+ # make fewer fetch calls; this could be parallel
97
+ i = need.pop(0)
98
+ sstart = i * self.blocksize
99
+ send = min(sstart + self.blocksize, self.size)
100
+ logger.debug(f"MMap get block #{i} ({sstart}-{send}")
101
+ self.cache[sstart:send] = self.fetcher(sstart, send)
102
+ self.blocks.add(i)
103
+
104
+ return self.cache[start:end]
105
+
106
+ def __getstate__(self):
107
+ state = self.__dict__.copy()
108
+ # Remove the unpicklable entries.
109
+ del state["cache"]
110
+ return state
111
+
112
+ def __setstate__(self, state):
113
+ # Restore instance attributes
114
+ self.__dict__.update(state)
115
+ self.cache = self._makefile()
116
+
117
+
118
+ class ReadAheadCache(BaseCache):
119
+ """Cache which reads only when we get beyond a block of data
120
+
121
+ This is a much simpler version of BytesCache, and does not attempt to
122
+ fill holes in the cache or keep fragments alive. It is best suited to
123
+ many small reads in a sequential order (e.g., reading lines from a file).
124
+ """
125
+
126
+ name = "readahead"
127
+
128
+ def __init__(self, blocksize, fetcher, size):
129
+ super().__init__(blocksize, fetcher, size)
130
+ self.cache = b""
131
+ self.start = 0
132
+ self.end = 0
133
+
134
+ def _fetch(self, start, end):
135
+ if start is None:
136
+ start = 0
137
+ if end is None or end > self.size:
138
+ end = self.size
139
+ if start >= self.size or start >= end:
140
+ return b""
141
+ l = end - start
142
+ if start >= self.start and end <= self.end:
143
+ # cache hit
144
+ return self.cache[start - self.start : end - self.start]
145
+ elif self.start <= start < self.end:
146
+ # partial hit
147
+ part = self.cache[start - self.start :]
148
+ l -= len(part)
149
+ start = self.end
150
+ else:
151
+ # miss
152
+ part = b""
153
+ end = min(self.size, end + self.blocksize)
154
+ self.cache = self.fetcher(start, end) # new block replaces old
155
+ self.start = start
156
+ self.end = self.start + len(self.cache)
157
+ return part + self.cache[:l]
158
+
159
+
160
+ class FirstChunkCache(BaseCache):
161
+ """Caches the first block of a file only
162
+
163
+ This may be useful for file types where the metadata is stored in the header,
164
+ but is randomly accessed.
165
+ """
166
+
167
+ name = "first"
168
+
169
+ def __init__(self, blocksize, fetcher, size):
170
+ super().__init__(blocksize, fetcher, size)
171
+ self.cache = None
172
+
173
+ def _fetch(self, start, end):
174
+ start = start or 0
175
+ end = end or self.size
176
+ if start < self.blocksize:
177
+ if self.cache is None:
178
+ if end > self.blocksize:
179
+ data = self.fetcher(0, end)
180
+ self.cache = data[: self.blocksize]
181
+ return data[start:]
182
+ self.cache = self.fetcher(0, self.blocksize)
183
+ part = self.cache[start:end]
184
+ if end > self.blocksize:
185
+ part += self.fetcher(self.blocksize, end)
186
+ return part
187
+ else:
188
+ return self.fetcher(start, end)
189
+
190
+
191
+ class BlockCache(BaseCache):
192
+ """
193
+ Cache holding memory as a set of blocks.
194
+
195
+ Requests are only ever made `blocksize` at a time, and are
196
+ stored in an LRU cache. The least recently accessed block is
197
+ discarded when more than `maxblocks` are stored.
198
+
199
+ Parameters
200
+ ----------
201
+ blocksize : int
202
+ The number of bytes to store in each block.
203
+ Requests are only ever made for `blocksize`, so this
204
+ should balance the overhead of making a request against
205
+ the granularity of the blocks.
206
+ fetcher : Callable
207
+ size : int
208
+ The total size of the file being cached.
209
+ maxblocks : int
210
+ The maximum number of blocks to cache for. The maximum memory
211
+ use for this cache is then ``blocksize * maxblocks``.
212
+ """
213
+
214
+ name = "blockcache"
215
+
216
+ def __init__(self, blocksize, fetcher, size, maxblocks=32):
217
+ super().__init__(blocksize, fetcher, size)
218
+ self.nblocks = math.ceil(size / blocksize)
219
+ self.maxblocks = maxblocks
220
+ self._fetch_block_cached = functools.lru_cache(maxblocks)(self._fetch_block)
221
+
222
+ def __repr__(self):
223
+ return "<BlockCache blocksize={}, size={}, nblocks={}>".format(
224
+ self.blocksize, self.size, self.nblocks
225
+ )
226
+
227
+ def cache_info(self):
228
+ """
229
+ The statistics on the block cache.
230
+
231
+ Returns
232
+ -------
233
+ NamedTuple
234
+ Returned directly from the LRU Cache used internally.
235
+ """
236
+ return self._fetch_block_cached.cache_info()
237
+
238
+ def __getstate__(self):
239
+ state = self.__dict__
240
+ del state["_fetch_block_cached"]
241
+ return state
242
+
243
+ def __setstate__(self, state):
244
+ self.__dict__.update(state)
245
+ self._fetch_block_cached = functools.lru_cache(state["maxblocks"])(
246
+ self._fetch_block
247
+ )
248
+
249
+ def _fetch(self, start, end):
250
+ if start is None:
251
+ start = 0
252
+ if end is None:
253
+ end = self.size
254
+ if start >= self.size or start >= end:
255
+ return b""
256
+
257
+ # byte position -> block numbers
258
+ start_block_number = start // self.blocksize
259
+ end_block_number = end // self.blocksize
260
+
261
+ # these are cached, so safe to do multiple calls for the same start and end.
262
+ for block_number in range(start_block_number, end_block_number + 1):
263
+ self._fetch_block_cached(block_number)
264
+
265
+ return self._read_cache(
266
+ start,
267
+ end,
268
+ start_block_number=start_block_number,
269
+ end_block_number=end_block_number,
270
+ )
271
+
272
+ def _fetch_block(self, block_number):
273
+ """
274
+ Fetch the block of data for `block_number`.
275
+ """
276
+ if block_number > self.nblocks:
277
+ raise ValueError(
278
+ "'block_number={}' is greater than the number of blocks ({})".format(
279
+ block_number, self.nblocks
280
+ )
281
+ )
282
+
283
+ start = block_number * self.blocksize
284
+ end = start + self.blocksize
285
+ logger.info("BlockCache fetching block %d", block_number)
286
+ block_contents = super()._fetch(start, end)
287
+ return block_contents
288
+
289
+ def _read_cache(self, start, end, start_block_number, end_block_number):
290
+ """
291
+ Read from our block cache.
292
+
293
+ Parameters
294
+ ----------
295
+ start, end : int
296
+ The start and end byte positions.
297
+ start_block_number, end_block_number : int
298
+ The start and end block numbers.
299
+ """
300
+ start_pos = start % self.blocksize
301
+ end_pos = end % self.blocksize
302
+
303
+ if start_block_number == end_block_number:
304
+ block = self._fetch_block_cached(start_block_number)
305
+ return block[start_pos:end_pos]
306
+
307
+ else:
308
+ # read from the initial
309
+ out = []
310
+ out.append(self._fetch_block_cached(start_block_number)[start_pos:])
311
+
312
+ # intermediate blocks
313
+ # Note: it'd be nice to combine these into one big request. However
314
+ # that doesn't play nicely with our LRU cache.
315
+ for block_number in range(start_block_number + 1, end_block_number):
316
+ out.append(self._fetch_block_cached(block_number))
317
+
318
+ # final block
319
+ out.append(self._fetch_block_cached(end_block_number)[:end_pos])
320
+
321
+ return b"".join(out)
322
+
323
+
324
+ class BytesCache(BaseCache):
325
+ """Cache which holds data in a in-memory bytes object
326
+
327
+ Implements read-ahead by the block size, for semi-random reads progressing
328
+ through the file.
329
+
330
+ Parameters
331
+ ----------
332
+ trim: bool
333
+ As we read more data, whether to discard the start of the buffer when
334
+ we are more than a blocksize ahead of it.
335
+ """
336
+
337
+ name = "bytes"
338
+
339
+ def __init__(self, blocksize, fetcher, size, trim=True):
340
+ super().__init__(blocksize, fetcher, size)
341
+ self.cache = b""
342
+ self.start = None
343
+ self.end = None
344
+ self.trim = trim
345
+
346
+ def _fetch(self, start, end):
347
+ # TODO: only set start/end after fetch, in case it fails?
348
+ # is this where retry logic might go?
349
+ if start is None:
350
+ start = 0
351
+ if end is None:
352
+ end = self.size
353
+ if start >= self.size or start >= end:
354
+ return b""
355
+ if (
356
+ self.start is not None
357
+ and start >= self.start
358
+ and self.end is not None
359
+ and end < self.end
360
+ ):
361
+ # cache hit: we have all the required data
362
+ offset = start - self.start
363
+ return self.cache[offset : offset + end - start]
364
+
365
+ if self.blocksize:
366
+ bend = min(self.size, end + self.blocksize)
367
+ else:
368
+ bend = end
369
+
370
+ if bend == start or start > self.size:
371
+ return b""
372
+
373
+ if (self.start is None or start < self.start) and (
374
+ self.end is None or end > self.end
375
+ ):
376
+ # First read, or extending both before and after
377
+ self.cache = self.fetcher(start, bend)
378
+ self.start = start
379
+ elif start < self.start:
380
+ if self.end - end > self.blocksize:
381
+ self.cache = self.fetcher(start, bend)
382
+ self.start = start
383
+ else:
384
+ new = self.fetcher(start, self.start)
385
+ self.start = start
386
+ self.cache = new + self.cache
387
+ elif bend > self.end:
388
+ if self.end > self.size:
389
+ pass
390
+ elif end - self.end > self.blocksize:
391
+ self.cache = self.fetcher(start, bend)
392
+ self.start = start
393
+ else:
394
+ new = self.fetcher(self.end, bend)
395
+ self.cache = self.cache + new
396
+
397
+ self.end = self.start + len(self.cache)
398
+ offset = start - self.start
399
+ out = self.cache[offset : offset + end - start]
400
+ if self.trim:
401
+ num = (self.end - self.start) // (self.blocksize + 1)
402
+ if num > 1:
403
+ self.start += self.blocksize * num
404
+ self.cache = self.cache[self.blocksize * num :]
405
+ return out
406
+
407
+ def __len__(self):
408
+ return len(self.cache)
409
+
410
+
411
+ class AllBytes(BaseCache):
412
+ """Cache entire contents of the file"""
413
+
414
+ name = "all"
415
+
416
+ def __init__(self, blocksize=None, fetcher=None, size=None, data=None):
417
+ super().__init__(blocksize, fetcher, size)
418
+ if data is None:
419
+ data = self.fetcher(0, self.size)
420
+ self.data = data
421
+
422
+ def _fetch(self, start, end):
423
+ return self.data[start:end]
424
+
425
+
426
+ class KnownPartsOfAFile(BaseCache):
427
+ """
428
+ Cache holding known file parts.
429
+
430
+ Parameters
431
+ ----------
432
+ blocksize: int
433
+ How far to read ahead in numbers of bytes
434
+ fetcher: func
435
+ Function of the form f(start, end) which gets bytes from remote as
436
+ specified
437
+ size: int
438
+ How big this file is
439
+ data: dict
440
+ A dictionary mapping explicit `(start, stop)` file-offset tuples
441
+ with known bytes.
442
+ strict: bool, default True
443
+ Whether to fetch reads that go beyond a known byte-range boundary.
444
+ If `False`, any read that ends outside a known part will be zero
445
+ padded. Note that zero padding will not be used for reads that
446
+ begin outside a known byte-range.
447
+ """
448
+
449
+ name = "parts"
450
+
451
+ def __init__(self, blocksize, fetcher, size, data={}, strict=True, **_):
452
+ super(KnownPartsOfAFile, self).__init__(blocksize, fetcher, size)
453
+ self.strict = strict
454
+
455
+ # simple consolidation of contiguous blocks
456
+ if data:
457
+ old_offsets = sorted(list(data.keys()))
458
+ offsets = [old_offsets[0]]
459
+ blocks = [data.pop(old_offsets[0])]
460
+ for start, stop in old_offsets[1:]:
461
+ start0, stop0 = offsets[-1]
462
+ if start == stop0:
463
+ offsets[-1] = (start0, stop)
464
+ blocks[-1] += data.pop((start, stop))
465
+ else:
466
+ offsets.append((start, stop))
467
+ blocks.append(data.pop((start, stop)))
468
+
469
+ self.data = dict(zip(offsets, blocks))
470
+ else:
471
+ self.data = data
472
+
473
+ def _fetch(self, start, stop):
474
+ out = b""
475
+ for (loc0, loc1), data in self.data.items():
476
+ # If self.strict=False, use zero-padded data
477
+ # for reads beyond the end of a "known" buffer
478
+ if loc0 <= start < loc1:
479
+ off = start - loc0
480
+ out = data[off : off + stop - start]
481
+ if not self.strict or loc0 <= stop <= loc1:
482
+ # The request is within a known range, or
483
+ # it begins within a known range, and we
484
+ # are allowed to pad reads beyond the
485
+ # buffer with zero
486
+ out += b"\x00" * (stop - start - len(out))
487
+ return out
488
+ else:
489
+ # The request ends outside a known range,
490
+ # and we are being "strict" about reads
491
+ # beyond the buffer
492
+ start = loc1
493
+ break
494
+
495
+ # We only get here if there is a request outside the
496
+ # known parts of the file. In an ideal world, this
497
+ # should never happen
498
+ if self.fetcher is None:
499
+ # We cannot fetch the data, so raise an error
500
+ raise ValueError(f"Read is outside the known file parts: {(start, stop)}. ")
501
+ # We can fetch the data, but should warn the user
502
+ # that this may be slow
503
+ warnings.warn(
504
+ f"Read is outside the known file parts: {(start, stop)}. "
505
+ f"IO/caching performance may be poor!"
506
+ )
507
+ logger.debug(f"KnownPartsOfAFile cache fetching {start}-{stop}")
508
+ return out + super()._fetch(start, stop)
509
+
510
+
511
+ caches = {
512
+ "none": BaseCache,
513
+ None: BaseCache,
514
+ "mmap": MMapCache,
515
+ "bytes": BytesCache,
516
+ "readahead": ReadAheadCache,
517
+ "block": BlockCache,
518
+ "first": FirstChunkCache,
519
+ "all": AllBytes,
520
+ "parts": KnownPartsOfAFile,
521
+ }
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/callbacks.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Callback:
2
+ """
3
+ Base class and interface for callback mechanism
4
+
5
+ This class can be used directly for monitoring file transfers by
6
+ providing ``callback=Callback(hooks=...)`` (see the ``hooks`` argument,
7
+ below), or subclassed for more specialised behaviour.
8
+
9
+ Parameters
10
+ ----------
11
+ size: int (optional)
12
+ Nominal quantity for the value that corresponds to a complete
13
+ transfer, e.g., total number of tiles or total number of
14
+ bytes
15
+ value: int (0)
16
+ Starting internal counter value
17
+ hooks: dict or None
18
+ A dict of named functions to be called on each update. The signature
19
+ of these must be ``f(size, value, **kwargs)``
20
+ """
21
+
22
+ def __init__(self, size=None, value=0, hooks=None, **kwargs):
23
+ self.size = size
24
+ self.value = value
25
+ self.hooks = hooks or {}
26
+ self.kw = kwargs
27
+
28
+ def set_size(self, size):
29
+ """
30
+ Set the internal maximum size attribute
31
+
32
+ Usually called if not initially set at instantiation. Note that this
33
+ triggers a ``call()``.
34
+
35
+ Parameters
36
+ ----------
37
+ size: int
38
+ """
39
+ self.size = size
40
+ self.call()
41
+
42
+ def absolute_update(self, value):
43
+ """
44
+ Set the internal value state
45
+
46
+ Triggers ``call()``
47
+
48
+ Parameters
49
+ ----------
50
+ value: int
51
+ """
52
+ self.value = value
53
+ self.call()
54
+
55
+ def relative_update(self, inc=1):
56
+ """
57
+ Delta increment the internal counter
58
+
59
+ Triggers ``call()``
60
+
61
+ Parameters
62
+ ----------
63
+ inc: int
64
+ """
65
+ self.value += inc
66
+ self.call()
67
+
68
+ def call(self, hook_name=None, **kwargs):
69
+ """
70
+ Execute hook(s) with current state
71
+
72
+ Each function is passed the internal size and current value
73
+
74
+ Parameters
75
+ ----------
76
+ hook_name: str or None
77
+ If given, execute on this hook
78
+ kwargs: passed on to (all) hook(s)
79
+ """
80
+ if not self.hooks:
81
+ return
82
+ kw = self.kw.copy()
83
+ kw.update(kwargs)
84
+ if hook_name:
85
+ if hook_name not in self.hooks:
86
+ return
87
+ return self.hooks[hook_name](self.size, self.value, **kw)
88
+ for hook in self.hooks.values() or []:
89
+ hook(self.size, self.value, **kw)
90
+
91
+ def wrap(self, iterable):
92
+ """
93
+ Wrap an iterable to call ``relative_update`` on each iterations
94
+
95
+ Parameters
96
+ ----------
97
+ iterable: Iterable
98
+ The iterable that is being wrapped
99
+ """
100
+ for item in iterable:
101
+ self.relative_update()
102
+ yield item
103
+
104
+ def branch(self, path_1, path_2, kwargs):
105
+ """
106
+ Set callbacks for child transfers
107
+
108
+ If this callback is operating at a higher level, e.g., put, which may
109
+ trigger transfers that can also be monitored. The passed kwargs are
110
+ to be *mutated* to add ``callback=``, if this class supports branching
111
+ to children.
112
+
113
+ Parameters
114
+ ----------
115
+ path_1: str
116
+ Child's source path
117
+ path_2: str
118
+ Child's destination path
119
+ kwargs: dict
120
+ arguments passed to child method, e.g., put_file.
121
+
122
+ Returns
123
+ -------
124
+
125
+ """
126
+ return None
127
+
128
+ def no_op(self, *_, **__):
129
+ pass
130
+
131
+ def __getattr__(self, item):
132
+ """
133
+ If undefined methods are called on this class, nothing happens
134
+ """
135
+ return self.no_op
136
+
137
+ @classmethod
138
+ def as_callback(cls, maybe_callback=None):
139
+ """Transform callback=... into Callback instance
140
+
141
+ For the special value of ``None``, return the global instance of
142
+ ``NoOpCallback``. This is an alternative to including
143
+ ``callback=_DEFAULT_CALLBACK`` directly in a method signature.
144
+ """
145
+ if maybe_callback is None:
146
+ return _DEFAULT_CALLBACK
147
+ return maybe_callback
148
+
149
+
150
+ class NoOpCallback(Callback):
151
+ """
152
+ This implementation of Callback does exactly nothing
153
+ """
154
+
155
+ def call(self, *args, **kwargs):
156
+ return None
157
+
158
+
159
+ class DotPrinterCallback(Callback):
160
+ """
161
+ Simple example Callback implementation
162
+
163
+ Almost identical to Callback with a hook that prints a char; here we
164
+ demonstrate how the outer layer may print "#" and the inner layer "."
165
+ """
166
+
167
+ def __init__(self, chr_to_print="#", **kwargs):
168
+ self.chr = chr_to_print
169
+ super().__init__(**kwargs)
170
+
171
+ def branch(self, path_1, path_2, kwargs):
172
+ """Mutate kwargs to add new instance with different print char"""
173
+ kwargs["callback"] = DotPrinterCallback(".")
174
+
175
+ def call(self, **kwargs):
176
+ """Just outputs a character"""
177
+ print(self.chr, end="")
178
+
179
+
180
+ class TqdmCallback(Callback):
181
+ """
182
+ A callback to display a progress bar using tqdm
183
+
184
+ Parameters
185
+ ----------
186
+ tqdm_kwargs : dict, (optional)
187
+ Any argument accepted by the tqdm constructor.
188
+ See the `tqdm doc <https://tqdm.github.io/docs/tqdm/#__init__>`_.
189
+ Will be forwarded to tqdm.
190
+
191
+ Examples
192
+ --------
193
+ >>> import fsspec
194
+ >>> from fsspec.callbacks import TqdmCallback
195
+ >>> fs = fsspec.filesystem("memory")
196
+ >>> path2distant_data = "/your-path"
197
+ >>> fs.upload(
198
+ ".",
199
+ path2distant_data,
200
+ recursive=True,
201
+ callback=TqdmCallback(),
202
+ )
203
+
204
+ You can forward args to tqdm using the `tqdm_kwargs` parameter.
205
+
206
+ >>> fs.upload(
207
+ ".",
208
+ path2distant_data,
209
+ recursive=True,
210
+ callback=TqdmCallback(tqdm_kwargs={"desc": "Your tqdm description"}),
211
+ )
212
+ """
213
+
214
+ def __init__(self, tqdm_kwargs=None, *args, **kwargs):
215
+ try:
216
+ import tqdm
217
+
218
+ self._tqdm = tqdm
219
+ except ImportError as exce:
220
+ raise ImportError(
221
+ "Using TqdmCallback requires tqdm to be installed"
222
+ ) from exce
223
+
224
+ self._tqdm_kwargs = tqdm_kwargs or {}
225
+ super().__init__(*args, **kwargs)
226
+
227
+ def set_size(self, size):
228
+ self.tqdm = self._tqdm.tqdm(total=size, **self._tqdm_kwargs)
229
+
230
+ def relative_update(self, inc=1):
231
+ self.tqdm.update(inc)
232
+
233
+ def __del__(self):
234
+ self.tqdm.close()
235
+ self.tqdm = None
236
+
237
+
238
+ _DEFAULT_CALLBACK = NoOpCallback()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/compression.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Helper functions for a standard streaming compression API"""
2
+ from bz2 import BZ2File
3
+ from zipfile import ZipFile
4
+
5
+ import fsspec.utils
6
+ from fsspec.spec import AbstractBufferedFile
7
+
8
+
9
+ def noop_file(file, mode, **kwargs):
10
+ return file
11
+
12
+
13
+ # TODO: files should also be available as contexts
14
+ # should be functions of the form func(infile, mode=, **kwargs) -> file-like
15
+ compr = {None: noop_file}
16
+
17
+
18
+ def register_compression(name, callback, extensions, force=False):
19
+ """Register an "inferable" file compression type.
20
+
21
+ Registers transparent file compression type for use with fsspec.open.
22
+ Compression can be specified by name in open, or "infer"-ed for any files
23
+ ending with the given extensions.
24
+
25
+ Args:
26
+ name: (str) The compression type name. Eg. "gzip".
27
+ callback: A callable of form (infile, mode, **kwargs) -> file-like.
28
+ Accepts an input file-like object, the target mode and kwargs.
29
+ Returns a wrapped file-like object.
30
+ extensions: (str, Iterable[str]) A file extension, or list of file
31
+ extensions for which to infer this compression scheme. Eg. "gz".
32
+ force: (bool) Force re-registration of compression type or extensions.
33
+
34
+ Raises:
35
+ ValueError: If name or extensions already registered, and not force.
36
+
37
+ """
38
+ if isinstance(extensions, str):
39
+ extensions = [extensions]
40
+
41
+ # Validate registration
42
+ if name in compr and not force:
43
+ raise ValueError("Duplicate compression registration: %s" % name)
44
+
45
+ for ext in extensions:
46
+ if ext in fsspec.utils.compressions and not force:
47
+ raise ValueError(
48
+ "Duplicate compression file extension: %s (%s)" % (ext, name)
49
+ )
50
+
51
+ compr[name] = callback
52
+
53
+ for ext in extensions:
54
+ fsspec.utils.compressions[ext] = name
55
+
56
+
57
+ def unzip(infile, mode="rb", filename=None, **kwargs):
58
+ if "r" not in mode:
59
+ filename = filename or "file"
60
+ z = ZipFile(infile, mode="w", **kwargs)
61
+ fo = z.open(filename, mode="w")
62
+ fo.close = lambda closer=fo.close: closer() or z.close()
63
+ return fo
64
+ z = ZipFile(infile)
65
+ if filename is None:
66
+ filename = z.namelist()[0]
67
+ return z.open(filename, mode="r", **kwargs)
68
+
69
+
70
+ register_compression("zip", unzip, "zip")
71
+ register_compression("bz2", BZ2File, "bz2")
72
+
73
+ try: # pragma: no cover
74
+ from isal import igzip
75
+
76
+ # igzip is meant to be used as a faster drop in replacement to gzip
77
+ # so its api and functions are the same as the stdlib’s module. Except
78
+ # where ISA-L does not support the same calls as zlib
79
+ # (See https://python-isal.readthedocs.io/).
80
+
81
+ register_compression("gzip", igzip.IGzipFile, "gz")
82
+ except ImportError:
83
+ from gzip import GzipFile
84
+
85
+ register_compression(
86
+ "gzip", lambda f, **kwargs: GzipFile(fileobj=f, **kwargs), "gz"
87
+ )
88
+
89
+ try:
90
+ from lzma import LZMAFile
91
+
92
+ register_compression("lzma", LZMAFile, "xz")
93
+ register_compression("xz", LZMAFile, "xz", force=True)
94
+ except ImportError:
95
+ pass
96
+
97
+ try:
98
+ import lzmaffi
99
+
100
+ register_compression("lzma", lzmaffi.LZMAFile, "xz", force=True)
101
+ register_compression("xz", lzmaffi.LZMAFile, "xz", force=True)
102
+ except ImportError:
103
+ pass
104
+
105
+
106
+ class SnappyFile(AbstractBufferedFile):
107
+ def __init__(self, infile, mode, **kwargs):
108
+ import snappy
109
+
110
+ super().__init__(
111
+ fs=None, path="snappy", mode=mode.strip("b") + "b", size=999999999, **kwargs
112
+ )
113
+ self.infile = infile
114
+ if "r" in mode:
115
+ self.codec = snappy.StreamDecompressor()
116
+ else:
117
+ self.codec = snappy.StreamCompressor()
118
+
119
+ def _upload_chunk(self, final=False):
120
+ self.buffer.seek(0)
121
+ out = self.codec.add_chunk(self.buffer.read())
122
+ self.infile.write(out)
123
+ return True
124
+
125
+ def seek(self, loc, whence=0):
126
+ raise NotImplementedError("SnappyFile is not seekable")
127
+
128
+ def seekable(self):
129
+ return False
130
+
131
+ def _fetch_range(self, start, end):
132
+ """Get the specified set of bytes from remote"""
133
+ data = self.infile.read(end - start)
134
+ return self.codec.decompress(data)
135
+
136
+
137
+ try:
138
+ import snappy
139
+
140
+ snappy.compress
141
+ # Snappy may use the .sz file extension, but this is not part of the
142
+ # standard implementation.
143
+ register_compression("snappy", SnappyFile, [])
144
+
145
+ except (ImportError, NameError):
146
+ pass
147
+
148
+ try:
149
+ import lz4.frame
150
+
151
+ register_compression("lz4", lz4.frame.open, "lz4")
152
+ except ImportError:
153
+ pass
154
+
155
+ try:
156
+ import zstandard as zstd
157
+
158
+ def zstandard_file(infile, mode="rb"):
159
+ if "r" in mode:
160
+ cctx = zstd.ZstdDecompressor()
161
+ return cctx.stream_reader(infile)
162
+ else:
163
+ cctx = zstd.ZstdCompressor(level=10)
164
+ return cctx.stream_writer(infile)
165
+
166
+ register_compression("zstd", zstandard_file, "zst")
167
+ except ImportError:
168
+ pass
169
+
170
+
171
+ def available_compressions():
172
+ """Return a list of the implemented compressions."""
173
+ return list(compr)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/config.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import configparser
2
+ import json
3
+ import os
4
+
5
+ conf = {}
6
+ default_conf_dir = os.path.join(os.path.expanduser("~"), ".config/fsspec")
7
+ conf_dir = os.environ.get("FSSPEC_CONFIG_DIR", default_conf_dir)
8
+
9
+
10
+ def set_conf_env(conf_dict, envdict=os.environ):
11
+ """Set config values from environment variables
12
+
13
+ Looks for variable of the form ``FSSPEC_<protocol>_<kwarg>``.
14
+ There is no attempt to convert strings, but the kwarg keys will
15
+ be lower-cased.
16
+
17
+ Parameters
18
+ ----------
19
+ conf_dict : dict(str, dict)
20
+ This dict will be mutated
21
+ envdict : dict-like(str, str)
22
+ Source for the values - usually the real environment
23
+ """
24
+ for key in envdict:
25
+ if key.startswith("FSSPEC"):
26
+ if key.count("_") < 2:
27
+ continue
28
+ _, proto, kwarg = key.split("_", 2)
29
+ conf_dict.setdefault(proto.lower(), {})[kwarg.lower()] = envdict[key]
30
+
31
+
32
+ def set_conf_files(cdir, conf_dict):
33
+ """Set config values from files
34
+
35
+ Scans for INI and JSON files in the given dictionary, and uses their
36
+ contents to set the config. In case of repeated values, later values
37
+ win.
38
+
39
+ In the case of INI files, all values are strings, and these will not
40
+ be converted.
41
+
42
+ Parameters
43
+ ----------
44
+ cdir : str
45
+ Directory to search
46
+ conf_dict : dict(str, dict)
47
+ This dict will be mutated
48
+ """
49
+ if not os.path.isdir(cdir):
50
+ return
51
+ allfiles = sorted(os.listdir(cdir))
52
+ for fn in allfiles:
53
+ if fn.endswith(".ini"):
54
+ ini = configparser.ConfigParser()
55
+ ini.read(os.path.join(cdir, fn))
56
+ for key in ini:
57
+ if key == "DEFAULT":
58
+ continue
59
+ conf_dict.setdefault(key, {}).update(dict(ini[key]))
60
+ if fn.endswith(".json"):
61
+ with open(os.path.join(cdir, fn)) as f:
62
+ js = json.load(f)
63
+ for key in js:
64
+ conf_dict.setdefault(key, {}).update(dict(js[key]))
65
+
66
+
67
+ def apply_config(cls, kwargs, conf_dict=None):
68
+ """Supply default values for kwargs when instantiating class
69
+
70
+ Augments the passed kwargs, by finding entries in the config dict
71
+ which match the classes ``.protocol`` attribute (one or more str)
72
+
73
+ Parameters
74
+ ----------
75
+ cls : file system implementation
76
+ kwargs : dict
77
+ conf_dict : dict of dict
78
+ Typically this is the global configuration
79
+
80
+ Returns
81
+ -------
82
+ dict : the modified set of kwargs
83
+ """
84
+ if conf_dict is None:
85
+ conf_dict = conf
86
+ protos = cls.protocol if isinstance(cls.protocol, (tuple, list)) else [cls.protocol]
87
+ kw = {}
88
+ for proto in protos:
89
+ # default kwargs from the current state of the config
90
+ if proto in conf_dict:
91
+ kw.update(conf_dict[proto])
92
+ # explicit kwargs always win
93
+ kw.update(**kwargs)
94
+ kwargs = kw
95
+ return kwargs
96
+
97
+
98
+ set_conf_files(conf_dir, conf)
99
+ set_conf_env(conf)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/conftest.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import sys
5
+ import time
6
+
7
+ import pytest
8
+
9
+ import fsspec
10
+ from fsspec.implementations.cached import CachingFileSystem
11
+
12
+
13
+ @pytest.fixture()
14
+ def m():
15
+ """
16
+ Fixture providing a memory filesystem.
17
+ """
18
+ m = fsspec.filesystem("memory")
19
+ m.store.clear()
20
+ m.pseudo_dirs.clear()
21
+ m.pseudo_dirs.append("")
22
+ try:
23
+ yield m
24
+ finally:
25
+ m.store.clear()
26
+ m.pseudo_dirs.clear()
27
+ m.pseudo_dirs.append("")
28
+
29
+
30
+ @pytest.fixture
31
+ def ftp_writable(tmpdir):
32
+ """
33
+ Fixture providing a writable FTP filesystem.
34
+ """
35
+ pytest.importorskip("pyftpdlib")
36
+ from fsspec.implementations.ftp import FTPFileSystem
37
+
38
+ FTPFileSystem.clear_instance_cache() # remove lingering connections
39
+ CachingFileSystem.clear_instance_cache()
40
+ d = str(tmpdir)
41
+ with open(os.path.join(d, "out"), "wb") as f:
42
+ f.write(b"hello" * 10000)
43
+ P = subprocess.Popen(
44
+ [sys.executable, "-m", "pyftpdlib", "-d", d, "-u", "user", "-P", "pass", "-w"]
45
+ )
46
+ try:
47
+ time.sleep(1)
48
+ yield "localhost", 2121, "user", "pass"
49
+ finally:
50
+ P.terminate()
51
+ P.wait()
52
+ try:
53
+ shutil.rmtree(tmpdir)
54
+ except Exception:
55
+ pass
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/core.py ADDED
@@ -0,0 +1,707 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import absolute_import, division, print_function
2
+
3
+ import io
4
+ import logging
5
+ import os
6
+ import re
7
+ from glob import has_magic
8
+
9
+ # for backwards compat, we export cache things from here too
10
+ from .caching import ( # noqa: F401
11
+ BaseCache,
12
+ BlockCache,
13
+ BytesCache,
14
+ MMapCache,
15
+ ReadAheadCache,
16
+ caches,
17
+ )
18
+ from .compression import compr
19
+ from .registry import filesystem, get_filesystem_class
20
+ from .utils import (
21
+ IOWrapper,
22
+ _unstrip_protocol,
23
+ build_name_function,
24
+ infer_compression,
25
+ stringify_path,
26
+ update_storage_options,
27
+ )
28
+
29
+ logger = logging.getLogger("fsspec")
30
+
31
+
32
+ class OpenFile(object):
33
+ """
34
+ File-like object to be used in a context
35
+
36
+ Can layer (buffered) text-mode and compression over any file-system, which
37
+ are typically binary-only.
38
+
39
+ These instances are safe to serialize, as the low-level file object
40
+ is not created until invoked using `with`.
41
+
42
+ Parameters
43
+ ----------
44
+ fs: FileSystem
45
+ The file system to use for opening the file. Should be a subclass or duck-type
46
+ with ``fsspec.spec.AbstractFileSystem``
47
+ path: str
48
+ Location to open
49
+ mode: str like 'rb', optional
50
+ Mode of the opened file
51
+ compression: str or None, optional
52
+ Compression to apply
53
+ encoding: str or None, optional
54
+ The encoding to use if opened in text mode.
55
+ errors: str or None, optional
56
+ How to handle encoding errors if opened in text mode.
57
+ newline: None or str
58
+ Passed to TextIOWrapper in text mode, how to handle line endings.
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ fs,
64
+ path,
65
+ mode="rb",
66
+ compression=None,
67
+ encoding=None,
68
+ errors=None,
69
+ newline=None,
70
+ ):
71
+ self.fs = fs
72
+ self.path = path
73
+ self.mode = mode
74
+ self.compression = get_compression(path, compression)
75
+ self.encoding = encoding
76
+ self.errors = errors
77
+ self.newline = newline
78
+ self.fobjects = []
79
+
80
+ def __reduce__(self):
81
+ return (
82
+ OpenFile,
83
+ (
84
+ self.fs,
85
+ self.path,
86
+ self.mode,
87
+ self.compression,
88
+ self.encoding,
89
+ self.errors,
90
+ self.newline,
91
+ ),
92
+ )
93
+
94
+ def __repr__(self):
95
+ return "<OpenFile '{}'>".format(self.path)
96
+
97
+ def __fspath__(self):
98
+ # may raise if cannot be resolved to local file
99
+ return self.open().__fspath__()
100
+
101
+ def __enter__(self):
102
+ mode = self.mode.replace("t", "").replace("b", "") + "b"
103
+
104
+ f = self.fs.open(self.path, mode=mode)
105
+
106
+ self.fobjects = [f]
107
+
108
+ if self.compression is not None:
109
+ compress = compr[self.compression]
110
+ f = compress(f, mode=mode[0])
111
+ self.fobjects.append(f)
112
+
113
+ if "b" not in self.mode:
114
+ # assume, for example, that 'r' is equivalent to 'rt' as in builtin
115
+ f = io.TextIOWrapper(
116
+ f, encoding=self.encoding, errors=self.errors, newline=self.newline
117
+ )
118
+ self.fobjects.append(f)
119
+
120
+ return self.fobjects[-1]
121
+
122
+ def __exit__(self, *args):
123
+ self.close()
124
+
125
+ def __del__(self):
126
+ if hasattr(self, "fobjects"):
127
+ self.fobjects.clear() # may cause cleanup of objects and close files
128
+
129
+ @property
130
+ def full_name(self):
131
+ return _unstrip_protocol(self.path, self.fs)
132
+
133
+ def open(self):
134
+ """Materialise this as a real open file without context
135
+
136
+ The file should be explicitly closed to avoid enclosed file
137
+ instances persisting. This code-path monkey-patches the file-like
138
+ objects, so they can close even if the parent OpenFile object has already
139
+ been deleted; but a with-context is better style.
140
+ """
141
+ out = self.__enter__()
142
+ closer = out.close
143
+ fobjects = self.fobjects.copy()[:-1]
144
+ mode = self.mode
145
+
146
+ def close():
147
+ # this func has no reference to
148
+ closer() # original close bound method of the final file-like
149
+ _close(fobjects, mode) # call close on other dependent file-likes
150
+
151
+ try:
152
+ out.close = close
153
+ except AttributeError:
154
+ out = IOWrapper(out, lambda: _close(fobjects, mode))
155
+
156
+ return out
157
+
158
+ def close(self):
159
+ """Close all encapsulated file objects"""
160
+ _close(self.fobjects, self.mode)
161
+
162
+
163
+ class OpenFiles(list):
164
+ """List of OpenFile instances
165
+
166
+ Can be used in a single context, which opens and closes all of the
167
+ contained files. Normal list access to get the elements works as
168
+ normal.
169
+
170
+ A special case is made for caching filesystems - the files will
171
+ be down/uploaded together at the start or end of the context, and
172
+ this may happen concurrently, if the target filesystem supports it.
173
+ """
174
+
175
+ def __init__(self, *args, mode="rb", fs=None):
176
+ self.mode = mode
177
+ self.fs = fs
178
+ self.files = []
179
+ super().__init__(*args)
180
+
181
+ def __enter__(self):
182
+ if self.fs is None:
183
+ raise ValueError("Context has already been used")
184
+
185
+ fs = self.fs
186
+ while True:
187
+ if hasattr(fs, "open_many"):
188
+ # check for concurrent cache download; or set up for upload
189
+ self.files = fs.open_many(self)
190
+ return self.files
191
+ if hasattr(fs, "fs") and fs.fs is not None:
192
+ fs = fs.fs
193
+ else:
194
+ break
195
+ return [s.__enter__() for s in self]
196
+
197
+ def __exit__(self, *args):
198
+ fs = self.fs
199
+ if "r" not in self.mode:
200
+ while True:
201
+ if hasattr(fs, "open_many"):
202
+ # check for concurrent cache upload
203
+ fs.commit_many(self.files)
204
+ self.files.clear()
205
+ return
206
+ if hasattr(fs, "fs") and fs.fs is not None:
207
+ fs = fs.fs
208
+ else:
209
+ break
210
+ [s.__exit__(*args) for s in self]
211
+
212
+ def __getitem__(self, item):
213
+ out = super().__getitem__(item)
214
+ if isinstance(item, slice):
215
+ return OpenFiles(out, mode=self.mode, fs=self.fs)
216
+ return out
217
+
218
+ def __repr__(self):
219
+ return "<List of %s OpenFile instances>" % len(self)
220
+
221
+
222
+ def _close(fobjects, mode):
223
+ for f in reversed(fobjects):
224
+ if "r" not in mode and not f.closed:
225
+ f.flush()
226
+ f.close()
227
+ fobjects.clear()
228
+
229
+
230
+ def open_files(
231
+ urlpath,
232
+ mode="rb",
233
+ compression=None,
234
+ encoding="utf8",
235
+ errors=None,
236
+ name_function=None,
237
+ num=1,
238
+ protocol=None,
239
+ newline=None,
240
+ auto_mkdir=True,
241
+ expand=True,
242
+ **kwargs,
243
+ ):
244
+ """Given a path or paths, return a list of ``OpenFile`` objects.
245
+
246
+ For writing, a str path must contain the "*" character, which will be filled
247
+ in by increasing numbers, e.g., "part*" -> "part1", "part2" if num=2.
248
+
249
+ For either reading or writing, can instead provide explicit list of paths.
250
+
251
+ Parameters
252
+ ----------
253
+ urlpath: string or list
254
+ Absolute or relative filepath(s). Prefix with a protocol like ``s3://``
255
+ to read from alternative filesystems. To read from multiple files you
256
+ can pass a globstring or a list of paths, with the caveat that they
257
+ must all have the same protocol.
258
+ mode: 'rb', 'wt', etc.
259
+ compression: string or None
260
+ If given, open file using compression codec. Can either be a compression
261
+ name (a key in ``fsspec.compression.compr``) or "infer" to guess the
262
+ compression from the filename suffix.
263
+ encoding: str
264
+ For text mode only
265
+ errors: None or str
266
+ Passed to TextIOWrapper in text mode
267
+ name_function: function or None
268
+ if opening a set of files for writing, those files do not yet exist,
269
+ so we need to generate their names by formatting the urlpath for
270
+ each sequence number
271
+ num: int [1]
272
+ if writing mode, number of files we expect to create (passed to
273
+ name+function)
274
+ protocol: str or None
275
+ If given, overrides the protocol found in the URL.
276
+ newline: bytes or None
277
+ Used for line terminator in text mode. If None, uses system default;
278
+ if blank, uses no translation.
279
+ auto_mkdir: bool (True)
280
+ If in write mode, this will ensure the target directory exists before
281
+ writing, by calling ``fs.mkdirs(exist_ok=True)``.
282
+ expand: bool
283
+ **kwargs: dict
284
+ Extra options that make sense to a particular storage connection, e.g.
285
+ host, port, username, password, etc.
286
+
287
+ Examples
288
+ --------
289
+ >>> files = open_files('2015-*-*.csv') # doctest: +SKIP
290
+ >>> files = open_files(
291
+ ... 's3://bucket/2015-*-*.csv.gz', compression='gzip'
292
+ ... ) # doctest: +SKIP
293
+
294
+ Returns
295
+ -------
296
+ An ``OpenFiles`` instance, which is a list of ``OpenFile`` objects that can
297
+ be used as a single context
298
+ """
299
+ fs, fs_token, paths = get_fs_token_paths(
300
+ urlpath,
301
+ mode,
302
+ num=num,
303
+ name_function=name_function,
304
+ storage_options=kwargs,
305
+ protocol=protocol,
306
+ expand=expand,
307
+ )
308
+ if "r" not in mode and auto_mkdir:
309
+ parents = {fs._parent(path) for path in paths}
310
+ [fs.makedirs(parent, exist_ok=True) for parent in parents]
311
+ return OpenFiles(
312
+ [
313
+ OpenFile(
314
+ fs,
315
+ path,
316
+ mode=mode,
317
+ compression=compression,
318
+ encoding=encoding,
319
+ errors=errors,
320
+ newline=newline,
321
+ )
322
+ for path in paths
323
+ ],
324
+ mode=mode,
325
+ fs=fs,
326
+ )
327
+
328
+
329
+ def _un_chain(path, kwargs):
330
+ if isinstance(path, (tuple, list)):
331
+ bits = [_un_chain(p, kwargs) for p in path]
332
+ out = []
333
+ for pbit in zip(*bits):
334
+ paths, protocols, kwargs = zip(*pbit)
335
+ if len(set(protocols)) > 1:
336
+ raise ValueError("Protocol mismatch in URL chain")
337
+ if len(set(paths)) == 1:
338
+ paths = paths[0]
339
+ else:
340
+ paths = list(paths)
341
+ out.append([paths, protocols[0], kwargs[0]])
342
+ return out
343
+ x = re.compile(".*[^a-z]+.*") # test for non protocol-like single word
344
+ bits = (
345
+ [p if "://" in p or x.match(p) else p + "://" for p in path.split("::")]
346
+ if "::" in path
347
+ else [path]
348
+ )
349
+ if len(bits) < 2:
350
+ return []
351
+ # [[url, protocol, kwargs], ...]
352
+ out = []
353
+ previous_bit = None
354
+ for bit in reversed(bits):
355
+ protocol = split_protocol(bit)[0] or "file"
356
+ cls = get_filesystem_class(protocol)
357
+ extra_kwargs = cls._get_kwargs_from_urls(bit)
358
+ kws = kwargs.get(protocol, {})
359
+ kw = dict(**extra_kwargs, **kws)
360
+ bit = cls._strip_protocol(bit)
361
+ if (
362
+ protocol in {"blockcache", "filecache", "simplecache"}
363
+ and "target_protocol" not in kw
364
+ ):
365
+ bit = previous_bit
366
+ out.append((bit, protocol, kw))
367
+ previous_bit = bit
368
+ out = list(reversed(out))
369
+ return out
370
+
371
+
372
+ def url_to_fs(url, **kwargs):
373
+ """
374
+ Turn fully-qualified and potentially chained URL into filesystem instance
375
+
376
+ Parameters
377
+ ----------
378
+ url : str
379
+ The fsspec-compatible URL
380
+ **kwargs: dict
381
+ Extra options that make sense to a particular storage connection, e.g.
382
+ host, port, username, password, etc.
383
+
384
+ Returns
385
+ -------
386
+ filesystem : FileSystem
387
+ The new filesystem discovered from ``url`` and created with
388
+ ``**kwargs``.
389
+ urlpath : str
390
+ The file-systems-specific URL for ``url``.
391
+ """
392
+ chain = _un_chain(url, kwargs)
393
+ if len(chain) > 1:
394
+ inkwargs = {}
395
+ # Reverse iterate the chain, creating a nested target_* structure
396
+ for i, ch in enumerate(reversed(chain)):
397
+ urls, protocol, kw = ch
398
+ if i == len(chain) - 1:
399
+ inkwargs = dict(**kw, **inkwargs)
400
+ continue
401
+ inkwargs["target_options"] = dict(**kw, **inkwargs)
402
+ inkwargs["target_protocol"] = protocol
403
+ inkwargs["fo"] = urls
404
+ urlpath, protocol, _ = chain[0]
405
+ fs = filesystem(protocol, **inkwargs)
406
+ else:
407
+ protocol = split_protocol(url)[0]
408
+ cls = get_filesystem_class(protocol)
409
+
410
+ options = cls._get_kwargs_from_urls(url)
411
+ update_storage_options(options, kwargs)
412
+ fs = cls(**options)
413
+ urlpath = fs._strip_protocol(url)
414
+ return fs, urlpath
415
+
416
+
417
+ def open(
418
+ urlpath,
419
+ mode="rb",
420
+ compression=None,
421
+ encoding="utf8",
422
+ errors=None,
423
+ protocol=None,
424
+ newline=None,
425
+ **kwargs,
426
+ ):
427
+ """Given a path or paths, return one ``OpenFile`` object.
428
+
429
+ Parameters
430
+ ----------
431
+ urlpath: string or list
432
+ Absolute or relative filepath. Prefix with a protocol like ``s3://``
433
+ to read from alternative filesystems. Should not include glob
434
+ character(s).
435
+ mode: 'rb', 'wt', etc.
436
+ compression: string or None
437
+ If given, open file using compression codec. Can either be a compression
438
+ name (a key in ``fsspec.compression.compr``) or "infer" to guess the
439
+ compression from the filename suffix.
440
+ encoding: str
441
+ For text mode only
442
+ errors: None or str
443
+ Passed to TextIOWrapper in text mode
444
+ protocol: str or None
445
+ If given, overrides the protocol found in the URL.
446
+ newline: bytes or None
447
+ Used for line terminator in text mode. If None, uses system default;
448
+ if blank, uses no translation.
449
+ **kwargs: dict
450
+ Extra options that make sense to a particular storage connection, e.g.
451
+ host, port, username, password, etc.
452
+
453
+ Examples
454
+ --------
455
+ >>> openfile = open('2015-01-01.csv') # doctest: +SKIP
456
+ >>> openfile = open(
457
+ ... 's3://bucket/2015-01-01.csv.gz', compression='gzip'
458
+ ... ) # doctest: +SKIP
459
+ >>> with openfile as f:
460
+ ... df = pd.read_csv(f) # doctest: +SKIP
461
+ ...
462
+
463
+ Returns
464
+ -------
465
+ ``OpenFile`` object.
466
+ """
467
+ return open_files(
468
+ urlpath=[urlpath],
469
+ mode=mode,
470
+ compression=compression,
471
+ encoding=encoding,
472
+ errors=errors,
473
+ protocol=protocol,
474
+ newline=newline,
475
+ expand=False,
476
+ **kwargs,
477
+ )[0]
478
+
479
+
480
+ def open_local(url, mode="rb", **storage_options):
481
+ """Open file(s) which can be resolved to local
482
+
483
+ For files which either are local, or get downloaded upon open
484
+ (e.g., by file caching)
485
+
486
+ Parameters
487
+ ----------
488
+ url: str or list(str)
489
+ mode: str
490
+ Must be read mode
491
+ storage_options:
492
+ passed on to FS for or used by open_files (e.g., compression)
493
+ """
494
+ if "r" not in mode:
495
+ raise ValueError("Can only ensure local files when reading")
496
+ of = open_files(url, mode=mode, **storage_options)
497
+ if not getattr(of[0].fs, "local_file", False):
498
+ raise ValueError(
499
+ "open_local can only be used on a filesystem which"
500
+ " has attribute local_file=True"
501
+ )
502
+ with of as files:
503
+ paths = [f.name for f in files]
504
+ if isinstance(url, str) and not has_magic(url):
505
+ return paths[0]
506
+ return paths
507
+
508
+
509
+ def get_compression(urlpath, compression):
510
+ if compression == "infer":
511
+ compression = infer_compression(urlpath)
512
+ if compression is not None and compression not in compr:
513
+ raise ValueError("Compression type %s not supported" % compression)
514
+ return compression
515
+
516
+
517
+ def split_protocol(urlpath):
518
+ """Return protocol, path pair"""
519
+ urlpath = stringify_path(urlpath)
520
+ if "://" in urlpath:
521
+ protocol, path = urlpath.split("://", 1)
522
+ if len(protocol) > 1:
523
+ # excludes Windows paths
524
+ return protocol, path
525
+ return None, urlpath
526
+
527
+
528
+ def strip_protocol(urlpath):
529
+ """Return only path part of full URL, according to appropriate backend"""
530
+ protocol, _ = split_protocol(urlpath)
531
+ cls = get_filesystem_class(protocol)
532
+ return cls._strip_protocol(urlpath)
533
+
534
+
535
+ def expand_paths_if_needed(paths, mode, num, fs, name_function):
536
+ """Expand paths if they have a ``*`` in them (write mode) or any of ``*?[]``
537
+ in them (read mode).
538
+
539
+ :param paths: list of paths
540
+ mode: str
541
+ Mode in which to open files.
542
+ num: int
543
+ If opening in writing mode, number of files we expect to create.
544
+ fs: filesystem object
545
+ name_function: callable
546
+ If opening in writing mode, this callable is used to generate path
547
+ names. Names are generated for each partition by
548
+ ``urlpath.replace('*', name_function(partition_index))``.
549
+ :return: list of paths
550
+ """
551
+ expanded_paths = []
552
+ paths = list(paths)
553
+
554
+ if "w" in mode: # read mode
555
+ if sum([1 for p in paths if "*" in p]) > 1:
556
+ raise ValueError(
557
+ "When writing data, only one filename mask can be specified."
558
+ )
559
+ num = max(num, len(paths))
560
+
561
+ for curr_path in paths:
562
+ if "*" in curr_path:
563
+ # expand using name_function
564
+ expanded_paths.extend(_expand_paths(curr_path, name_function, num))
565
+ else:
566
+ expanded_paths.append(curr_path)
567
+ # if we generated more paths that asked for, trim the list
568
+ if len(expanded_paths) > num:
569
+ expanded_paths = expanded_paths[:num]
570
+
571
+ else: # read mode
572
+ for curr_path in paths:
573
+ if has_magic(curr_path):
574
+ # expand using glob
575
+ expanded_paths.extend(fs.glob(curr_path))
576
+ else:
577
+ expanded_paths.append(curr_path)
578
+
579
+ return expanded_paths
580
+
581
+
582
+ def get_fs_token_paths(
583
+ urlpath,
584
+ mode="rb",
585
+ num=1,
586
+ name_function=None,
587
+ storage_options=None,
588
+ protocol=None,
589
+ expand=True,
590
+ ):
591
+ """Filesystem, deterministic token, and paths from a urlpath and options.
592
+
593
+ Parameters
594
+ ----------
595
+ urlpath: string or iterable
596
+ Absolute or relative filepath, URL (may include protocols like
597
+ ``s3://``), or globstring pointing to data.
598
+ mode: str, optional
599
+ Mode in which to open files.
600
+ num: int, optional
601
+ If opening in writing mode, number of files we expect to create.
602
+ name_function: callable, optional
603
+ If opening in writing mode, this callable is used to generate path
604
+ names. Names are generated for each partition by
605
+ ``urlpath.replace('*', name_function(partition_index))``.
606
+ storage_options: dict, optional
607
+ Additional keywords to pass to the filesystem class.
608
+ protocol: str or None
609
+ To override the protocol specifier in the URL
610
+ expand: bool
611
+ Expand string paths for writing, assuming the path is a directory
612
+ """
613
+ if isinstance(urlpath, (list, tuple, set)):
614
+ if not urlpath:
615
+ raise ValueError("empty urlpath sequence")
616
+ urlpath = [stringify_path(u) for u in urlpath]
617
+ else:
618
+ urlpath = stringify_path(urlpath)
619
+ chain = _un_chain(urlpath, storage_options or {})
620
+ if len(chain) > 1:
621
+ inkwargs = {}
622
+ # Reverse iterate the chain, creating a nested target_* structure
623
+ for i, ch in enumerate(reversed(chain)):
624
+ urls, nested_protocol, kw = ch
625
+ if i == len(chain) - 1:
626
+ inkwargs = dict(**kw, **inkwargs)
627
+ continue
628
+ inkwargs["target_options"] = dict(**kw, **inkwargs)
629
+ inkwargs["target_protocol"] = nested_protocol
630
+ inkwargs["fo"] = urls
631
+ paths, protocol, _ = chain[0]
632
+ fs = filesystem(protocol, **inkwargs)
633
+ if isinstance(paths, (list, tuple, set)):
634
+ paths = [fs._strip_protocol(u) for u in paths]
635
+ else:
636
+ paths = fs._strip_protocol(paths)
637
+ else:
638
+ if isinstance(urlpath, (list, tuple, set)):
639
+ protocols, paths = zip(*map(split_protocol, urlpath))
640
+ if protocol is None:
641
+ protocol = protocols[0]
642
+ if not all(p == protocol for p in protocols):
643
+ raise ValueError(
644
+ "When specifying a list of paths, all paths must "
645
+ "share the same protocol"
646
+ )
647
+ cls = get_filesystem_class(protocol)
648
+ optionss = list(map(cls._get_kwargs_from_urls, urlpath))
649
+ paths = [cls._strip_protocol(u) for u in urlpath]
650
+ options = optionss[0]
651
+ if not all(o == options for o in optionss):
652
+ raise ValueError(
653
+ "When specifying a list of paths, all paths must "
654
+ "share the same file-system options"
655
+ )
656
+ update_storage_options(options, storage_options)
657
+ fs = cls(**options)
658
+ else:
659
+ protocols = split_protocol(urlpath)[0]
660
+ protocol = protocol or protocols
661
+ cls = get_filesystem_class(protocol)
662
+ options = cls._get_kwargs_from_urls(urlpath)
663
+ paths = cls._strip_protocol(urlpath)
664
+ update_storage_options(options, storage_options)
665
+ fs = cls(**options)
666
+
667
+ if isinstance(paths, (list, tuple, set)):
668
+ paths = expand_paths_if_needed(paths, mode, num, fs, name_function)
669
+ else:
670
+ if "w" in mode and expand:
671
+ paths = _expand_paths(paths, name_function, num)
672
+ elif "*" in paths:
673
+ paths = [f for f in sorted(fs.glob(paths)) if not fs.isdir(f)]
674
+ else:
675
+ paths = [paths]
676
+
677
+ return fs, fs._fs_token, paths
678
+
679
+
680
+ def _expand_paths(path, name_function, num):
681
+ if isinstance(path, str):
682
+ if path.count("*") > 1:
683
+ raise ValueError("Output path spec must contain exactly one '*'.")
684
+ elif "*" not in path:
685
+ path = os.path.join(path, "*.part")
686
+
687
+ if name_function is None:
688
+ name_function = build_name_function(num - 1)
689
+
690
+ paths = [path.replace("*", name_function(i)) for i in range(num)]
691
+ if paths != sorted(paths):
692
+ logger.warning(
693
+ "In order to preserve order between partitions"
694
+ " paths created with ``name_function`` should "
695
+ "sort to partition order"
696
+ )
697
+ elif isinstance(path, (tuple, list)):
698
+ assert len(path) == num
699
+ paths = list(path)
700
+ else:
701
+ raise ValueError(
702
+ "Path should be either\n"
703
+ "1. A list of paths: ['foo.json', 'bar.json', ...]\n"
704
+ "2. A directory: 'foo/\n"
705
+ "3. A path with a '*' in it: 'foo.*.json'"
706
+ )
707
+ return paths
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/dircache.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from collections.abc import MutableMapping
3
+ from functools import lru_cache
4
+
5
+
6
+ class DirCache(MutableMapping):
7
+ """
8
+ Caching of directory listings, in a structure like::
9
+
10
+ {"path0": [
11
+ {"name": "path0/file0",
12
+ "size": 123,
13
+ "type": "file",
14
+ ...
15
+ },
16
+ {"name": "path0/file1",
17
+ },
18
+ ...
19
+ ],
20
+ "path1": [...]
21
+ }
22
+
23
+ Parameters to this class control listing expiry or indeed turn
24
+ caching off
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ use_listings_cache=True,
30
+ listings_expiry_time=None,
31
+ max_paths=None,
32
+ **kwargs,
33
+ ):
34
+ """
35
+
36
+ Parameters
37
+ ----------
38
+ use_listings_cache: bool
39
+ If False, this cache never returns items, but always reports KeyError,
40
+ and setting items has no effect
41
+ listings_expiry_time: int or float (optional)
42
+ Time in seconds that a listing is considered valid. If None,
43
+ listings do not expire.
44
+ max_paths: int (optional)
45
+ The number of most recent listings that are considered valid; 'recent'
46
+ refers to when the entry was set.
47
+ """
48
+ self._cache = {}
49
+ self._times = {}
50
+ if max_paths:
51
+ self._q = lru_cache(max_paths + 1)(lambda key: self._cache.pop(key, None))
52
+ self.use_listings_cache = use_listings_cache
53
+ self.listings_expiry_time = listings_expiry_time
54
+ self.max_paths = max_paths
55
+
56
+ def __getitem__(self, item):
57
+ if self.listings_expiry_time is not None:
58
+ if self._times.get(item, 0) - time.time() < -self.listings_expiry_time:
59
+ del self._cache[item]
60
+ if self.max_paths:
61
+ self._q(item)
62
+ return self._cache[item] # maybe raises KeyError
63
+
64
+ def clear(self):
65
+ self._cache.clear()
66
+
67
+ def __len__(self):
68
+ return len(self._cache)
69
+
70
+ def __contains__(self, item):
71
+ try:
72
+ self[item]
73
+ return True
74
+ except KeyError:
75
+ return False
76
+
77
+ def __setitem__(self, key, value):
78
+ if not self.use_listings_cache:
79
+ return
80
+ if self.max_paths:
81
+ self._q(key)
82
+ self._cache[key] = value
83
+ if self.listings_expiry_time is not None:
84
+ self._times[key] = time.time()
85
+
86
+ def __delitem__(self, key):
87
+ del self._cache[key]
88
+
89
+ def __iter__(self):
90
+ return (k for k in self._cache if k in self)
91
+
92
+ def __reduce__(self):
93
+ return (
94
+ DirCache,
95
+ (self.use_listings_cache, self.listings_expiry_time, self.max_paths),
96
+ )
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/exceptions.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ fsspec user-defined exception classes
3
+ """
4
+ import asyncio
5
+
6
+
7
+ class BlocksizeMismatchError(ValueError):
8
+ """
9
+ Raised when a cached file is opened with a different blocksize than it was
10
+ written with
11
+ """
12
+
13
+ ...
14
+
15
+
16
+ class FSTimeoutError(asyncio.TimeoutError):
17
+ """
18
+ Raised when a fsspec function timed out occurs
19
+ """
20
+
21
+ ...
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/fuse.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import logging
3
+ import os
4
+ import stat
5
+ import threading
6
+ import time
7
+ from errno import EIO, ENOENT
8
+
9
+ from fuse import FUSE, FuseOSError, LoggingMixIn, Operations
10
+
11
+ from fsspec import __version__
12
+ from fsspec.core import url_to_fs
13
+
14
+ logger = logging.getLogger("fsspec.fuse")
15
+
16
+
17
+ class FUSEr(Operations):
18
+ def __init__(self, fs, path, ready_file=False):
19
+ self.fs = fs
20
+ self.cache = {}
21
+ self.root = path.rstrip("/") + "/"
22
+ self.counter = 0
23
+ logger.info("Starting FUSE at %s", path)
24
+ self._ready_file = ready_file
25
+
26
+ def getattr(self, path, fh=None):
27
+ logger.debug("getattr %s", path)
28
+ if self._ready_file and path in ["/.fuse_ready", ".fuse_ready"]:
29
+ return {"type": "file", "st_size": 5}
30
+
31
+ path = "".join([self.root, path.lstrip("/")]).rstrip("/")
32
+ try:
33
+ info = self.fs.info(path)
34
+ except FileNotFoundError:
35
+ raise FuseOSError(ENOENT)
36
+
37
+ data = {"st_uid": info.get("uid", 1000), "st_gid": info.get("gid", 1000)}
38
+ perm = info.get("mode", 0o777)
39
+
40
+ if info["type"] != "file":
41
+ data["st_mode"] = stat.S_IFDIR | perm
42
+ data["st_size"] = 0
43
+ data["st_blksize"] = 0
44
+ else:
45
+ data["st_mode"] = stat.S_IFREG | perm
46
+ data["st_size"] = info["size"]
47
+ data["st_blksize"] = 5 * 2**20
48
+ data["st_nlink"] = 1
49
+ data["st_atime"] = time.time()
50
+ data["st_ctime"] = time.time()
51
+ data["st_mtime"] = time.time()
52
+ return data
53
+
54
+ def readdir(self, path, fh):
55
+ logger.debug("readdir %s", path)
56
+ path = "".join([self.root, path.lstrip("/")])
57
+ files = self.fs.ls(path, False)
58
+ files = [os.path.basename(f.rstrip("/")) for f in files]
59
+ return [".", ".."] + files
60
+
61
+ def mkdir(self, path, mode):
62
+ path = "".join([self.root, path.lstrip("/")])
63
+ self.fs.mkdir(path)
64
+ return 0
65
+
66
+ def rmdir(self, path):
67
+ path = "".join([self.root, path.lstrip("/")])
68
+ self.fs.rmdir(path)
69
+ return 0
70
+
71
+ def read(self, path, size, offset, fh):
72
+ logger.debug("read %s", (path, size, offset))
73
+ if self._ready_file and path in ["/.fuse_ready", ".fuse_ready"]:
74
+ # status indicator
75
+ return b"ready"
76
+
77
+ f = self.cache[fh]
78
+ f.seek(offset)
79
+ out = f.read(size)
80
+ return out
81
+
82
+ def write(self, path, data, offset, fh):
83
+ logger.debug("write %s", (path, offset))
84
+ f = self.cache[fh]
85
+ f.seek(offset)
86
+ f.write(data)
87
+ return len(data)
88
+
89
+ def create(self, path, flags, fi=None):
90
+ logger.debug("create %s", (path, flags))
91
+ fn = "".join([self.root, path.lstrip("/")])
92
+ self.fs.touch(fn) # OS will want to get attributes immediately
93
+ f = self.fs.open(fn, "wb")
94
+ self.cache[self.counter] = f
95
+ self.counter += 1
96
+ return self.counter - 1
97
+
98
+ def open(self, path, flags):
99
+ logger.debug("open %s", (path, flags))
100
+ fn = "".join([self.root, path.lstrip("/")])
101
+ if flags % 2 == 0:
102
+ # read
103
+ mode = "rb"
104
+ else:
105
+ # write/create
106
+ mode = "wb"
107
+ self.cache[self.counter] = self.fs.open(fn, mode)
108
+ self.counter += 1
109
+ return self.counter - 1
110
+
111
+ def truncate(self, path, length, fh=None):
112
+ fn = "".join([self.root, path.lstrip("/")])
113
+ if length != 0:
114
+ raise NotImplementedError
115
+ # maybe should be no-op since open with write sets size to zero anyway
116
+ self.fs.touch(fn)
117
+
118
+ def unlink(self, path):
119
+ fn = "".join([self.root, path.lstrip("/")])
120
+ try:
121
+ self.fs.rm(fn, False)
122
+ except (IOError, FileNotFoundError):
123
+ raise FuseOSError(EIO)
124
+
125
+ def release(self, path, fh):
126
+ try:
127
+ if fh in self.cache:
128
+ f = self.cache[fh]
129
+ f.close()
130
+ self.cache.pop(fh)
131
+ except Exception as e:
132
+ print(e)
133
+ return 0
134
+
135
+ def chmod(self, path, mode):
136
+ if hasattr(self.fs, "chmod"):
137
+ path = "".join([self.root, path.lstrip("/")])
138
+ return self.fs.chmod(path, mode)
139
+ raise NotImplementedError
140
+
141
+
142
+ def run(
143
+ fs,
144
+ path,
145
+ mount_point,
146
+ foreground=True,
147
+ threads=False,
148
+ ready_file=False,
149
+ ops_class=FUSEr,
150
+ ):
151
+ """Mount stuff in a local directory
152
+
153
+ This uses fusepy to make it appear as if a given path on an fsspec
154
+ instance is in fact resident within the local file-system.
155
+
156
+ This requires that fusepy by installed, and that FUSE be available on
157
+ the system (typically requiring a package to be installed with
158
+ apt, yum, brew, etc.).
159
+
160
+ Parameters
161
+ ----------
162
+ fs: file-system instance
163
+ From one of the compatible implementations
164
+ path: str
165
+ Location on that file-system to regard as the root directory to
166
+ mount. Note that you typically should include the terminating "/"
167
+ character.
168
+ mount_point: str
169
+ An empty directory on the local file-system where the contents of
170
+ the remote path will appear.
171
+ foreground: bool
172
+ Whether or not calling this function will block. Operation will
173
+ typically be more stable if True.
174
+ threads: bool
175
+ Whether or not to create threads when responding to file operations
176
+ within the mounter directory. Operation will typically be more
177
+ stable if False.
178
+ ready_file: bool
179
+ Whether the FUSE process is ready. The `.fuse_ready` file will
180
+ exist in the `mount_point` directory if True. Debugging purpose.
181
+ ops_class: FUSEr or Subclass of FUSEr
182
+ To override the default behavior of FUSEr. For Example, logging
183
+ to file.
184
+
185
+ """
186
+ func = lambda: FUSE(
187
+ ops_class(fs, path, ready_file=ready_file),
188
+ mount_point,
189
+ nothreads=not threads,
190
+ foreground=foreground,
191
+ )
192
+ if not foreground:
193
+ th = threading.Thread(target=func)
194
+ th.daemon = True
195
+ th.start()
196
+ return th
197
+ else: # pragma: no cover
198
+ try:
199
+ func()
200
+ except KeyboardInterrupt:
201
+ pass
202
+
203
+
204
+ def main(args):
205
+ """Mount filesystem from chained URL to MOUNT_POINT.
206
+
207
+ Examples:
208
+
209
+ python3 -m fsspec.fuse memory /usr/share /tmp/mem
210
+
211
+ python3 -m fsspec.fuse local /tmp/source /tmp/local \\
212
+ -l /tmp/fsspecfuse.log
213
+
214
+ You can also mount chained-URLs and use special settings:
215
+
216
+ python3 -m fsspec.fuse 'filecache::zip::file://data.zip' \\
217
+ / /tmp/zip \\
218
+ -o 'filecache-cache_storage=/tmp/simplecache'
219
+
220
+ You can specify the type of the setting by using `[int]` or `[bool]`,
221
+ (`true`, `yes`, `1` represents the Boolean value `True`):
222
+
223
+ python3 -m fsspec.fuse 'simplecache::ftp://ftp1.at.proftpd.org' \\
224
+ /historic/packages/RPMS /tmp/ftp \\
225
+ -o 'simplecache-cache_storage=/tmp/simplecache' \\
226
+ -o 'simplecache-check_files=false[bool]' \\
227
+ -o 'ftp-listings_expiry_time=60[int]' \\
228
+ -o 'ftp-username=anonymous' \\
229
+ -o 'ftp-password=xieyanbo'
230
+ """
231
+
232
+ class RawDescriptionArgumentParser(argparse.ArgumentParser):
233
+ def format_help(self):
234
+ usage = super(RawDescriptionArgumentParser, self).format_help()
235
+ parts = usage.split("\n\n")
236
+ parts[1] = self.description.rstrip()
237
+ return "\n\n".join(parts)
238
+
239
+ parser = RawDescriptionArgumentParser(prog="fsspec.fuse", description=main.__doc__)
240
+ parser.add_argument("--version", action="version", version=__version__)
241
+ parser.add_argument("url", type=str, help="fs url")
242
+ parser.add_argument("source_path", type=str, help="source directory in fs")
243
+ parser.add_argument("mount_point", type=str, help="local directory")
244
+ parser.add_argument(
245
+ "-o",
246
+ "--option",
247
+ action="append",
248
+ help="Any options of protocol included in the chained URL",
249
+ )
250
+ parser.add_argument(
251
+ "-l", "--log-file", type=str, help="Logging FUSE debug info (Default: '')"
252
+ )
253
+ parser.add_argument(
254
+ "-f",
255
+ "--foreground",
256
+ action="store_false",
257
+ help="Running in foreground or not (Default: False)",
258
+ )
259
+ parser.add_argument(
260
+ "-t",
261
+ "--threads",
262
+ action="store_false",
263
+ help="Running with threads support (Default: False)",
264
+ )
265
+ parser.add_argument(
266
+ "-r",
267
+ "--ready-file",
268
+ action="store_false",
269
+ help="The `.fuse_ready` file will exist after FUSE is ready. "
270
+ "(Debugging purpose, Default: False)",
271
+ )
272
+ args = parser.parse_args(args)
273
+
274
+ kwargs = {}
275
+ for item in args.option or []:
276
+ key, sep, value = item.partition("=")
277
+ if not sep:
278
+ parser.error(message="Wrong option: {!r}".format(item))
279
+ val = value.lower()
280
+ if val.endswith("[int]"):
281
+ value = int(value[: -len("[int]")])
282
+ elif val.endswith("[bool]"):
283
+ value = val[: -len("[bool]")] in ["1", "yes", "true"]
284
+
285
+ if "-" in key:
286
+ fs_name, setting_name = key.split("-", 1)
287
+ if fs_name in kwargs:
288
+ kwargs[fs_name][setting_name] = value
289
+ else:
290
+ kwargs[fs_name] = {setting_name: value}
291
+ else:
292
+ kwargs[key] = value
293
+
294
+ if args.log_file:
295
+ logging.basicConfig(
296
+ level=logging.DEBUG,
297
+ filename=args.log_file,
298
+ format="%(asctime)s %(message)s",
299
+ )
300
+
301
+ class LoggingFUSEr(FUSEr, LoggingMixIn):
302
+ pass
303
+
304
+ fuser = LoggingFUSEr
305
+ else:
306
+ fuser = FUSEr
307
+
308
+ fs, url_path = url_to_fs(args.url, **kwargs)
309
+ logger.debug("Mounting %s to %s", url_path, str(args.mount_point))
310
+ run(
311
+ fs,
312
+ args.source_path,
313
+ args.mount_point,
314
+ foreground=args.foreground,
315
+ threads=args.threads,
316
+ ready_file=args.ready_file,
317
+ ops_class=fuser,
318
+ )
319
+
320
+
321
+ if __name__ == "__main__":
322
+ import sys
323
+
324
+ main(sys.argv[1:])
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/generic.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+
3
+ from .asyn import AsyncFileSystem
4
+ from .callbacks import _DEFAULT_CALLBACK
5
+ from .core import filesystem, get_filesystem_class, split_protocol
6
+
7
+ _generic_fs = {}
8
+
9
+
10
+ def set_generic_fs(protocol, **storage_options):
11
+ _generic_fs[protocol] = filesystem(protocol, **storage_options)
12
+
13
+
14
+ default_method = "default"
15
+
16
+
17
+ def _resolve_fs(url, method=None, protocol=None, storage_options=None):
18
+ """Pick instance of backend FS"""
19
+ method = method or default_method
20
+ protocol = protocol or split_protocol(url)[0]
21
+ storage_options = storage_options or {}
22
+ if method == "default":
23
+ return filesystem(protocol)
24
+ if method == "generic":
25
+ return _generic_fs[protocol]
26
+ if method == "current":
27
+ cls = get_filesystem_class(protocol)
28
+ return cls.current()
29
+ if method == "options":
30
+ return filesystem(protocol, **storage_options.get(protocol, {}))
31
+ raise ValueError(f"Unknown FS resolution method: {method}")
32
+
33
+
34
+ class GenericFileSystem(AsyncFileSystem):
35
+ """Wrapper over all other FS types
36
+
37
+ <experimental!>
38
+
39
+ This implementation is a single unified interface to be able to run FS operations
40
+ over generic URLs, and dispatch to the specific implementations using the URL
41
+ protocol prefix.
42
+
43
+ Note: instances of this FS are always async, even if you never use it with any async
44
+ backend.
45
+ """
46
+
47
+ protocol = "generic" # there is no real reason to ever use a protocol with this FS
48
+
49
+ def __init__(self, default_method=None, **kwargs):
50
+ """
51
+
52
+ Parameters
53
+ ----------
54
+ default_method: str (optional)
55
+ Defines how to configure backend FS instances. Options are:
56
+ - "default" (you get this with None): instantiate like FSClass(), with no
57
+ extra arguments; this is the default instance of that FS, and can be
58
+ configured via the config system
59
+ - "generic": takes instances from the `_generic_fs` dict in this module,
60
+ which you must populate before use. Keys are by protocol
61
+ - "current": takes the most recently instantiated version of each FS
62
+ - "options": expect ``storage_options`` to be passed along with every call.
63
+ """
64
+ self.method = default_method
65
+ super(GenericFileSystem, self).__init__(**kwargs)
66
+
67
+ async def _info(
68
+ self, url, method=None, protocol=None, storage_options=None, fs=None, **kwargs
69
+ ):
70
+ fs = fs or _resolve_fs(url, method or self.method, protocol, storage_options)
71
+ if fs.async_impl:
72
+ out = await fs._info(url, **kwargs)
73
+ else:
74
+ out = fs.info(url, **kwargs)
75
+ out["name"] = fs.unstrip_protocol(out["name"])
76
+ return out
77
+
78
+ async def _ls(
79
+ self,
80
+ url,
81
+ method=None,
82
+ protocol=None,
83
+ storage_options=None,
84
+ fs=None,
85
+ detail=True,
86
+ **kwargs,
87
+ ):
88
+ fs = fs or _resolve_fs(url, method or self.method, protocol, storage_options)
89
+ if fs.async_impl:
90
+ out = await fs._ls(url, detail=True, **kwargs)
91
+ else:
92
+ out = fs.ls(url, detail=True, **kwargs)
93
+ for o in out:
94
+ o["name"] = fs.unstrip_protocol(o["name"])
95
+ if detail:
96
+ return out
97
+ else:
98
+ return [o["name"] for o in out]
99
+
100
+ async def _rm(
101
+ self, url, method=None, protocol=None, storage_options=None, fs=None, **kwargs
102
+ ):
103
+ fs = fs or _resolve_fs(url, method or self.method, protocol, storage_options)
104
+ if fs.async_impl:
105
+ await fs._rm(url, **kwargs)
106
+ else:
107
+ fs.rm(url, **kwargs)
108
+
109
+ async def _cp_file(
110
+ self,
111
+ url,
112
+ url2,
113
+ method=None,
114
+ protocol=None,
115
+ storage_options=None,
116
+ fs=None,
117
+ method2=None,
118
+ protocol2=None,
119
+ storage_options2=None,
120
+ fs2=None,
121
+ blocksize=2**20,
122
+ callback=_DEFAULT_CALLBACK,
123
+ **kwargs,
124
+ ):
125
+ fs = fs or _resolve_fs(url, method or self.method, protocol, storage_options)
126
+ fs2 = fs2 or _resolve_fs(
127
+ url2, method2 or self.method, protocol2, storage_options2
128
+ )
129
+ if fs is fs2:
130
+ # pure remote
131
+ if fs.async_impl:
132
+ return await fs._cp_file(url, url2, **kwargs)
133
+ else:
134
+ return fs.cp_file(url, url2, **kwargs)
135
+ kw = {"blocksize": 0, "cache_type": "none"}
136
+ try:
137
+ f1 = (
138
+ await fs.open_async(url, "rb")
139
+ if hasattr(fs, "open_async")
140
+ else fs.open(url, "rb", **kw)
141
+ )
142
+ callback.set_size(maybe_await(f1.size))
143
+ f2 = (
144
+ await fs2.open_async(url2, "wb")
145
+ if hasattr(fs2, "open_async")
146
+ else fs2.open(url2, "wb", **kw)
147
+ )
148
+ while f1.size is None or f2.tell() < f1.size:
149
+ data = await maybe_await(f1.read(blocksize))
150
+ if f1.size is None and not data:
151
+ break
152
+ await maybe_await(f2.write(data))
153
+ callback.absolute_update(f2.tell())
154
+ finally:
155
+ try:
156
+ await maybe_await(f2.close())
157
+ await maybe_await(f1.close())
158
+ except NameError:
159
+ # fail while opening f1 or f2
160
+ pass
161
+
162
+
163
+ async def maybe_await(cor):
164
+ if inspect.iscoroutine(cor):
165
+ return await cor
166
+ else:
167
+ return cor
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/gui.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import contextlib
3
+ import logging
4
+ import os
5
+ import re
6
+
7
+ import panel as pn
8
+
9
+ from .core import OpenFile, get_filesystem_class, split_protocol
10
+ from .registry import known_implementations
11
+
12
+ pn.extension()
13
+ logger = logging.getLogger("fsspec.gui")
14
+
15
+
16
+ class SigSlot(object):
17
+ """Signal-slot mixin, for Panel event passing
18
+
19
+ Include this class in a widget manager's superclasses to be able to
20
+ register events and callbacks on Panel widgets managed by that class.
21
+
22
+ The method ``_register`` should be called as widgets are added, and external
23
+ code should call ``connect`` to associate callbacks.
24
+
25
+ By default, all signals emit a DEBUG logging statement.
26
+ """
27
+
28
+ signals = [] # names of signals that this class may emit
29
+ # each of which must be set by _register for any new instance
30
+ slots = [] # names of actions that this class may respond to
31
+
32
+ # each of which must be a method name
33
+
34
+ def __init__(self):
35
+ self._ignoring_events = False
36
+ self._sigs = {}
37
+ self._map = {}
38
+ self._setup()
39
+
40
+ def _setup(self):
41
+ """Create GUI elements and register signals"""
42
+ self.panel = pn.pane.PaneBase()
43
+ # no signals to set up in the base class
44
+
45
+ def _register(
46
+ self, widget, name, thing="value", log_level=logging.DEBUG, auto=False
47
+ ):
48
+ """Watch the given attribute of a widget and assign it a named event
49
+
50
+ This is normally called at the time a widget is instantiated, in the
51
+ class which owns it.
52
+
53
+ Parameters
54
+ ----------
55
+ widget : pn.layout.Panel or None
56
+ Widget to watch. If None, an anonymous signal not associated with
57
+ any widget.
58
+ name : str
59
+ Name of this event
60
+ thing : str
61
+ Attribute of the given widget to watch
62
+ log_level : int
63
+ When the signal is triggered, a logging event of the given level
64
+ will be fired in the dfviz logger.
65
+ auto : bool
66
+ If True, automatically connects with a method in this class of the
67
+ same name.
68
+ """
69
+ if name not in self.signals:
70
+ raise ValueError("Attempt to assign an undeclared signal: %s" % name)
71
+ self._sigs[name] = {
72
+ "widget": widget,
73
+ "callbacks": [],
74
+ "thing": thing,
75
+ "log": log_level,
76
+ }
77
+ wn = "-".join(
78
+ [
79
+ getattr(widget, "name", str(widget)) if widget is not None else "none",
80
+ thing,
81
+ ]
82
+ )
83
+ self._map[wn] = name
84
+ if widget is not None:
85
+ widget.param.watch(self._signal, thing, onlychanged=True)
86
+ if auto and hasattr(self, name):
87
+ self.connect(name, getattr(self, name))
88
+
89
+ def _repr_mimebundle_(self, *args, **kwargs):
90
+ """Display in a notebook or a server"""
91
+ try:
92
+ return self.panel._repr_mimebundle_(*args, **kwargs)
93
+ except (ValueError, AttributeError):
94
+ raise NotImplementedError("Panel does not seem to be set " "up properly")
95
+
96
+ def connect(self, signal, slot):
97
+ """Associate call back with given event
98
+
99
+ The callback must be a function which takes the "new" value of the
100
+ watched attribute as the only parameter. If the callback return False,
101
+ this cancels any further processing of the given event.
102
+
103
+ Alternatively, the callback can be a string, in which case it means
104
+ emitting the correspondingly-named event (i.e., connect to self)
105
+ """
106
+ self._sigs[signal]["callbacks"].append(slot)
107
+
108
+ def _signal(self, event):
109
+ """This is called by a an action on a widget
110
+
111
+ Within an self.ignore_events context, nothing happens.
112
+
113
+ Tests can execute this method by directly changing the values of
114
+ widget components.
115
+ """
116
+ if not self._ignoring_events:
117
+ wn = "-".join([event.obj.name, event.name])
118
+ if wn in self._map and self._map[wn] in self._sigs:
119
+ self._emit(self._map[wn], event.new)
120
+
121
+ @contextlib.contextmanager
122
+ def ignore_events(self):
123
+ """Temporarily turn off events processing in this instance
124
+
125
+ (does not propagate to children)
126
+ """
127
+ self._ignoring_events = True
128
+ try:
129
+ yield
130
+ finally:
131
+ self._ignoring_events = False
132
+
133
+ def _emit(self, sig, value=None):
134
+ """An event happened, call its callbacks
135
+
136
+ This method can be used in tests to simulate message passing without
137
+ directly changing visual elements.
138
+
139
+ Calling of callbacks will halt whenever one returns False.
140
+ """
141
+ logger.log(self._sigs[sig]["log"], "{}: {}".format(sig, value))
142
+ for callback in self._sigs[sig]["callbacks"]:
143
+ if isinstance(callback, str):
144
+ self._emit(callback)
145
+ else:
146
+ try:
147
+ # running callbacks should not break the interface
148
+ ret = callback(value)
149
+ if ret is False:
150
+ break
151
+ except Exception as e:
152
+ logger.exception(
153
+ "Exception (%s) while executing callback for signal: %s"
154
+ "" % (e, sig)
155
+ )
156
+
157
+ def show(self, threads=False):
158
+ """Open a new browser tab and display this instance's interface"""
159
+ self.panel.show(threads=threads, verbose=False)
160
+ return self
161
+
162
+
163
+ class SingleSelect(SigSlot):
164
+ """A multiselect which only allows you to select one item for an event"""
165
+
166
+ signals = ["_selected", "selected"] # the first is internal
167
+ slots = ["set_options", "set_selection", "add", "clear", "select"]
168
+
169
+ def __init__(self, **kwargs):
170
+ self.kwargs = kwargs
171
+ super().__init__()
172
+
173
+ def _setup(self):
174
+ self.panel = pn.widgets.MultiSelect(**self.kwargs)
175
+ self._register(self.panel, "_selected", "value")
176
+ self._register(None, "selected")
177
+ self.connect("_selected", self.select_one)
178
+
179
+ def _signal(self, *args, **kwargs):
180
+ super()._signal(*args, **kwargs)
181
+
182
+ def select_one(self, *_):
183
+ with self.ignore_events():
184
+ val = [self.panel.value[-1]] if self.panel.value else []
185
+ self.panel.value = val
186
+ self._emit("selected", self.panel.value)
187
+
188
+ def set_options(self, options):
189
+ self.panel.options = options
190
+
191
+ def clear(self):
192
+ self.panel.options = []
193
+
194
+ @property
195
+ def value(self):
196
+ return self.panel.value
197
+
198
+ def set_selection(self, selection):
199
+ self.panel.value = [selection]
200
+
201
+
202
+ class FileSelector(SigSlot):
203
+ """Panel-based graphical file selector widget
204
+
205
+ Instances of this widget are interactive and can be displayed in jupyter by having
206
+ them as the output of a cell, or in a separate browser tab using ``.show()``.
207
+ """
208
+
209
+ signals = [
210
+ "protocol_changed",
211
+ "selection_changed",
212
+ "directory_entered",
213
+ "home_clicked",
214
+ "up_clicked",
215
+ "go_clicked",
216
+ "filters_changed",
217
+ ]
218
+ slots = ["set_filters", "go_home"]
219
+
220
+ def __init__(self, url=None, filters=None, ignore=None, kwargs=None):
221
+ """
222
+
223
+ Parameters
224
+ ----------
225
+ url : str (optional)
226
+ Initial value of the URL to populate the dialog; should include protocol
227
+ filters : list(str) (optional)
228
+ File endings to include in the listings. If not included, all files are
229
+ allowed. Does not affect directories.
230
+ If given, the endings will appear as checkboxes in the interface
231
+ ignore : list(str) (optional)
232
+ Regex(s) of file basename patterns to ignore, e.g., "\\." for typical
233
+ hidden files on posix
234
+ kwargs : dict (optional)
235
+ To pass to file system instance
236
+ """
237
+ if url:
238
+ self.init_protocol, url = split_protocol(url)
239
+ else:
240
+ self.init_protocol, url = "file", os.getcwd()
241
+ self.init_url = url
242
+ self.init_kwargs = kwargs or "{}"
243
+ self.filters = filters
244
+ self.ignore = [re.compile(i) for i in ignore or []]
245
+ self._fs = None
246
+ super().__init__()
247
+
248
+ def _setup(self):
249
+ self.url = pn.widgets.TextInput(
250
+ name="url",
251
+ value=self.init_url,
252
+ align="end",
253
+ sizing_mode="stretch_width",
254
+ width_policy="max",
255
+ )
256
+ self.protocol = pn.widgets.Select(
257
+ options=list(sorted(known_implementations)),
258
+ value=self.init_protocol,
259
+ name="protocol",
260
+ align="center",
261
+ )
262
+ self.kwargs = pn.widgets.TextInput(name="kwargs", value="{}", align="center")
263
+ self.go = pn.widgets.Button(name="⇨", align="end", width=45)
264
+ self.main = SingleSelect(size=10)
265
+ self.home = pn.widgets.Button(name="🏠", width=40, height=30, align="end")
266
+ self.up = pn.widgets.Button(name="‹", width=30, height=30, align="end")
267
+
268
+ self._register(self.protocol, "protocol_changed", auto=True)
269
+ self._register(self.go, "go_clicked", "clicks", auto=True)
270
+ self._register(self.up, "up_clicked", "clicks", auto=True)
271
+ self._register(self.home, "home_clicked", "clicks", auto=True)
272
+ self._register(None, "selection_changed")
273
+ self.main.connect("selected", self.selection_changed)
274
+ self._register(None, "directory_entered")
275
+ self.prev_protocol = self.protocol.value
276
+ self.prev_kwargs = self.storage_options
277
+
278
+ self.filter_sel = pn.widgets.CheckBoxGroup(
279
+ value=[], options=[], inline=False, align="end", width_policy="min"
280
+ )
281
+ self._register(self.filter_sel, "filters_changed", auto=True)
282
+
283
+ self.panel = pn.Column(
284
+ pn.Row(self.protocol, self.kwargs),
285
+ pn.Row(self.home, self.up, self.url, self.go, self.filter_sel),
286
+ self.main.panel,
287
+ )
288
+ self.set_filters(self.filters)
289
+ self.go_clicked()
290
+
291
+ def set_filters(self, filters=None):
292
+ self.filters = filters
293
+ if filters:
294
+ self.filter_sel.options = filters
295
+ self.filter_sel.value = filters
296
+ else:
297
+ self.filter_sel.options = []
298
+ self.filter_sel.value = []
299
+
300
+ @property
301
+ def storage_options(self):
302
+ """Value of the kwargs box as a dictionary"""
303
+ return ast.literal_eval(self.kwargs.value) or {}
304
+
305
+ @property
306
+ def fs(self):
307
+ """Current filesystem instance"""
308
+ if self._fs is None:
309
+ cls = get_filesystem_class(self.protocol.value)
310
+ self._fs = cls(**self.storage_options)
311
+ return self._fs
312
+
313
+ @property
314
+ def urlpath(self):
315
+ """URL of currently selected item"""
316
+ return (
317
+ (self.protocol.value + "://" + self.main.value[0])
318
+ if self.main.value
319
+ else None
320
+ )
321
+
322
+ def open_file(self, mode="rb", compression=None, encoding=None):
323
+ """Create OpenFile instance for the currently selected item
324
+
325
+ For example, in a notebook you might do something like
326
+
327
+ .. code-block::
328
+
329
+ [ ]: sel = FileSelector(); sel
330
+
331
+ # user selects their file
332
+
333
+ [ ]: with sel.open_file('rb') as f:
334
+ ... out = f.read()
335
+
336
+ Parameters
337
+ ----------
338
+ mode: str (optional)
339
+ Open mode for the file.
340
+ compression: str (optional)
341
+ The interact with the file as compressed. Set to 'infer' to guess
342
+ compression from the file ending
343
+ encoding: str (optional)
344
+ If using text mode, use this encoding; defaults to UTF8.
345
+ """
346
+ if self.urlpath is None:
347
+ raise ValueError("No file selected")
348
+ return OpenFile(self.fs, self.urlpath, mode, compression, encoding)
349
+
350
+ def filters_changed(self, values):
351
+ self.filters = values
352
+ self.go_clicked()
353
+
354
+ def selection_changed(self, *_):
355
+ if self.urlpath is None:
356
+ return
357
+ if self.fs.isdir(self.urlpath):
358
+ self.url.value = self.fs._strip_protocol(self.urlpath)
359
+ self.go_clicked()
360
+
361
+ def go_clicked(self, *_):
362
+ if (
363
+ self.prev_protocol != self.protocol.value
364
+ or self.prev_kwargs != self.storage_options
365
+ ):
366
+ self._fs = None # causes fs to be recreated
367
+ self.prev_protocol = self.protocol.value
368
+ self.prev_kwargs = self.storage_options
369
+ listing = sorted(
370
+ self.fs.ls(self.url.value, detail=True), key=lambda x: x["name"]
371
+ )
372
+ listing = [
373
+ l
374
+ for l in listing
375
+ if not any(i.match(l["name"].rsplit("/", 1)[-1]) for i in self.ignore)
376
+ ]
377
+ folders = {
378
+ "📁 " + o["name"].rsplit("/", 1)[-1]: o["name"]
379
+ for o in listing
380
+ if o["type"] == "directory"
381
+ }
382
+ files = {
383
+ "📄 " + o["name"].rsplit("/", 1)[-1]: o["name"]
384
+ for o in listing
385
+ if o["type"] == "file"
386
+ }
387
+ if self.filters:
388
+ files = {
389
+ k: v
390
+ for k, v in files.items()
391
+ if any(v.endswith(ext) for ext in self.filters)
392
+ }
393
+ self.main.set_options(dict(**folders, **files))
394
+
395
+ def protocol_changed(self, *_):
396
+ self._fs = None
397
+ self.main.options = []
398
+ self.url.value = ""
399
+
400
+ def home_clicked(self, *_):
401
+ self.protocol.value = self.init_protocol
402
+ self.kwargs.value = self.init_kwargs
403
+ self.url.value = self.init_url
404
+ self.go_clicked()
405
+
406
+ def up_clicked(self, *_):
407
+ self.url.value = self.fs._parent(self.url.value)
408
+ self.go_clicked()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/mapping.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import array
2
+ from collections.abc import MutableMapping
3
+
4
+ from .core import url_to_fs
5
+
6
+
7
+ class FSMap(MutableMapping):
8
+ """Wrap a FileSystem instance as a mutable wrapping.
9
+
10
+ The keys of the mapping become files under the given root, and the
11
+ values (which must be bytes) the contents of those files.
12
+
13
+ Parameters
14
+ ----------
15
+ root: string
16
+ prefix for all the files
17
+ fs: FileSystem instance
18
+ check: bool (=True)
19
+ performs a touch at the location, to check for write access.
20
+
21
+ Examples
22
+ --------
23
+ >>> fs = FileSystem(**parameters) # doctest: +SKIP
24
+ >>> d = FSMap('my-data/path/', fs) # doctest: +SKIP
25
+ or, more likely
26
+ >>> d = fs.get_mapper('my-data/path/')
27
+
28
+ >>> d['loc1'] = b'Hello World' # doctest: +SKIP
29
+ >>> list(d.keys()) # doctest: +SKIP
30
+ ['loc1']
31
+ >>> d['loc1'] # doctest: +SKIP
32
+ b'Hello World'
33
+ """
34
+
35
+ def __init__(self, root, fs, check=False, create=False, missing_exceptions=None):
36
+ self.fs = fs
37
+ self.root = fs._strip_protocol(root).rstrip(
38
+ "/"
39
+ ) # we join on '/' in _key_to_str
40
+ if missing_exceptions is None:
41
+ missing_exceptions = (
42
+ FileNotFoundError,
43
+ IsADirectoryError,
44
+ NotADirectoryError,
45
+ )
46
+ self.missing_exceptions = missing_exceptions
47
+ self.check = check
48
+ self.create = create
49
+ if create:
50
+ if not self.fs.exists(root):
51
+ self.fs.mkdir(root)
52
+ if check:
53
+ if not self.fs.exists(root):
54
+ raise ValueError(
55
+ "Path %s does not exist. Create "
56
+ " with the ``create=True`` keyword" % root
57
+ )
58
+ self.fs.touch(root + "/a")
59
+ self.fs.rm(root + "/a")
60
+
61
+ def clear(self):
62
+ """Remove all keys below root - empties out mapping"""
63
+ try:
64
+ self.fs.rm(self.root, True)
65
+ self.fs.mkdir(self.root)
66
+ except: # noqa: E722
67
+ pass
68
+
69
+ def getitems(self, keys, on_error="raise"):
70
+ """Fetch multiple items from the store
71
+
72
+ If the backend is async-able, this might proceed concurrently
73
+
74
+ Parameters
75
+ ----------
76
+ keys: list(str)
77
+ They keys to be fetched
78
+ on_error : "raise", "omit", "return"
79
+ If raise, an underlying exception will be raised (converted to KeyError
80
+ if the type is in self.missing_exceptions); if omit, keys with exception
81
+ will simply not be included in the output; if "return", all keys are
82
+ included in the output, but the value will be bytes or an exception
83
+ instance.
84
+
85
+ Returns
86
+ -------
87
+ dict(key, bytes|exception)
88
+ """
89
+ keys2 = [self._key_to_str(k) for k in keys]
90
+ oe = on_error if on_error == "raise" else "return"
91
+ try:
92
+ out = self.fs.cat(keys2, on_error=oe)
93
+ if isinstance(out, bytes):
94
+ out = {keys2[0]: out}
95
+ except self.missing_exceptions as e:
96
+ raise KeyError from e
97
+ out = {
98
+ k: (KeyError() if isinstance(v, self.missing_exceptions) else v)
99
+ for k, v in out.items()
100
+ }
101
+ return {
102
+ key: out[k2]
103
+ for key, k2 in zip(keys, keys2)
104
+ if on_error == "return" or not isinstance(out[k2], BaseException)
105
+ }
106
+
107
+ def setitems(self, values_dict):
108
+ """Set the values of multiple items in the store
109
+
110
+ Parameters
111
+ ----------
112
+ values_dict: dict(str, bytes)
113
+ """
114
+ values = {self._key_to_str(k): maybe_convert(v) for k, v in values_dict.items()}
115
+ self.fs.pipe(values)
116
+
117
+ def delitems(self, keys):
118
+ """Remove multiple keys from the store"""
119
+ self.fs.rm([self._key_to_str(k) for k in keys])
120
+
121
+ def _key_to_str(self, key):
122
+ """Generate full path for the key"""
123
+ if isinstance(key, (tuple, list)):
124
+ key = str(tuple(key))
125
+ else:
126
+ key = str(key)
127
+ return self.fs._strip_protocol("/".join([self.root, key]) if self.root else key)
128
+
129
+ def _str_to_key(self, s):
130
+ """Strip path of to leave key name"""
131
+ return s[len(self.root) :].lstrip("/")
132
+
133
+ def __getitem__(self, key, default=None):
134
+ """Retrieve data"""
135
+ k = self._key_to_str(key)
136
+ try:
137
+ result = self.fs.cat(k)
138
+ except self.missing_exceptions:
139
+ if default is not None:
140
+ return default
141
+ raise KeyError(key)
142
+ return result
143
+
144
+ def pop(self, key, default=None):
145
+ result = self.__getitem__(key, default)
146
+ try:
147
+ del self[key]
148
+ except KeyError:
149
+ pass
150
+ return result
151
+
152
+ def __setitem__(self, key, value):
153
+ """Store value in key"""
154
+ key = self._key_to_str(key)
155
+ self.fs.mkdirs(self.fs._parent(key), exist_ok=True)
156
+ self.fs.pipe_file(key, maybe_convert(value))
157
+
158
+ def __iter__(self):
159
+ return (self._str_to_key(x) for x in self.fs.find(self.root))
160
+
161
+ def __len__(self):
162
+ return len(self.fs.find(self.root))
163
+
164
+ def __delitem__(self, key):
165
+ """Remove key"""
166
+ try:
167
+ self.fs.rm(self._key_to_str(key))
168
+ except: # noqa: E722
169
+ raise KeyError
170
+
171
+ def __contains__(self, key):
172
+ """Does key exist in mapping?"""
173
+ path = self._key_to_str(key)
174
+ return self.fs.exists(path) and self.fs.isfile(path)
175
+
176
+ def __reduce__(self):
177
+ return FSMap, (self.root, self.fs, False, False, self.missing_exceptions)
178
+
179
+
180
+ def maybe_convert(value):
181
+ if isinstance(value, array.array) or hasattr(value, "__array__"):
182
+ # bytes-like things
183
+ if hasattr(value, "dtype") and value.dtype.kind in "Mm":
184
+ # The buffer interface doesn't support datetime64/timdelta64 numpy
185
+ # arrays
186
+ value = value.view("int64")
187
+ value = bytearray(memoryview(value))
188
+ return value
189
+
190
+
191
+ def get_mapper(
192
+ url="",
193
+ check=False,
194
+ create=False,
195
+ missing_exceptions=None,
196
+ alternate_root=None,
197
+ **kwargs,
198
+ ):
199
+ """Create key-value interface for given URL and options
200
+
201
+ The URL will be of the form "protocol://location" and point to the root
202
+ of the mapper required. All keys will be file-names below this location,
203
+ and their values the contents of each key.
204
+
205
+ Also accepts compound URLs like zip::s3://bucket/file.zip , see ``fsspec.open``.
206
+
207
+ Parameters
208
+ ----------
209
+ url: str
210
+ Root URL of mapping
211
+ check: bool
212
+ Whether to attempt to read from the location before instantiation, to
213
+ check that the mapping does exist
214
+ create: bool
215
+ Whether to make the directory corresponding to the root before
216
+ instantiating
217
+ missing_exceptions: None or tuple
218
+ If given, these exception types will be regarded as missing keys and
219
+ return KeyError when trying to read data. By default, you get
220
+ (FileNotFoundError, IsADirectoryError, NotADirectoryError)
221
+ alternate_root: None or str
222
+ In cases of complex URLs, the parser may fail to pick the correct part
223
+ for the mapper root, so this arg can override
224
+
225
+ Returns
226
+ -------
227
+ ``FSMap`` instance, the dict-like key-value store.
228
+ """
229
+ # Removing protocol here - could defer to each open() on the backend
230
+ fs, urlpath = url_to_fs(url, **kwargs)
231
+ root = alternate_root if alternate_root is not None else urlpath
232
+ return FSMap(root, fs, check, create, missing_exceptions=missing_exceptions)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/parquet.py ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import json
3
+ import warnings
4
+
5
+ from .core import url_to_fs
6
+ from .utils import merge_offset_ranges
7
+
8
+ # Parquet-Specific Utilities for fsspec
9
+ #
10
+ # Most of the functions defined in this module are NOT
11
+ # intended for public consumption. The only exception
12
+ # to this is `open_parquet_file`, which should be used
13
+ # place of `fs.open()` to open parquet-formatted files
14
+ # on remote file systems.
15
+
16
+
17
+ def open_parquet_file(
18
+ path,
19
+ mode="rb",
20
+ fs=None,
21
+ metadata=None,
22
+ columns=None,
23
+ row_groups=None,
24
+ storage_options=None,
25
+ strict=False,
26
+ engine="auto",
27
+ max_gap=64_000,
28
+ max_block=256_000_000,
29
+ footer_sample_size=1_000_000,
30
+ **kwargs,
31
+ ):
32
+ """
33
+ Return a file-like object for a single Parquet file.
34
+
35
+ The specified parquet `engine` will be used to parse the
36
+ footer metadata, and determine the required byte ranges
37
+ from the file. The target path will then be opened with
38
+ the "parts" (`KnownPartsOfAFile`) caching strategy.
39
+
40
+ Note that this method is intended for usage with remote
41
+ file systems, and is unlikely to improve parquet-read
42
+ performance on local file systems.
43
+
44
+ Parameters
45
+ ----------
46
+ path: str
47
+ Target file path.
48
+ mode: str, optional
49
+ Mode option to be passed through to `fs.open`. Default is "rb".
50
+ metadata: Any, optional
51
+ Parquet metadata object. Object type must be supported
52
+ by the backend parquet engine. For now, only the "fastparquet"
53
+ engine supports an explicit `ParquetFile` metadata object.
54
+ If a metadata object is supplied, the remote footer metadata
55
+ will not need to be transferred into local memory.
56
+ fs: AbstractFileSystem, optional
57
+ Filesystem object to use for opening the file. If nothing is
58
+ specified, an `AbstractFileSystem` object will be inferred.
59
+ engine : str, default "auto"
60
+ Parquet engine to use for metadata parsing. Allowed options
61
+ include "fastparquet", "pyarrow", and "auto". The specified
62
+ engine must be installed in the current environment. If
63
+ "auto" is specified, and both engines are installed,
64
+ "fastparquet" will take precedence over "pyarrow".
65
+ columns: list, optional
66
+ List of all column names that may be read from the file.
67
+ row_groups : list, optional
68
+ List of all row-groups that may be read from the file. This
69
+ may be a list of row-group indices (integers), or it may be
70
+ a list of `RowGroup` metadata objects (if the "fastparquet"
71
+ engine is used).
72
+ storage_options : dict, optional
73
+ Used to generate an `AbstractFileSystem` object if `fs` was
74
+ not specified.
75
+ strict : bool, optional
76
+ Whether the resulting `KnownPartsOfAFile` cache should
77
+ fetch reads that go beyond a known byte-range boundary.
78
+ If `False` (the default), any read that ends outside a
79
+ known part will be zero padded. Note that using
80
+ `strict=True` may be useful for debugging.
81
+ max_gap : int, optional
82
+ Neighboring byte ranges will only be merged when their
83
+ inter-range gap is <= `max_gap`. Default is 64KB.
84
+ max_block : int, optional
85
+ Neighboring byte ranges will only be merged when the size of
86
+ the aggregated range is <= `max_block`. Default is 256MB.
87
+ footer_sample_size : int, optional
88
+ Number of bytes to read from the end of the path to look
89
+ for the footer metadata. If the sampled bytes do not contain
90
+ the footer, a second read request will be required, and
91
+ performance will suffer. Default is 1MB.
92
+ **kwargs :
93
+ Optional key-word arguments to pass to `fs.open`
94
+ """
95
+
96
+ # Make sure we have an `AbstractFileSystem` object
97
+ # to work with
98
+ if fs is None:
99
+ fs = url_to_fs(path, **(storage_options or {}))[0]
100
+
101
+ # For now, `columns == []` not supported. Just use
102
+ # default `open` command with `path` input
103
+ if columns is not None and len(columns) == 0:
104
+ return fs.open(path, mode=mode)
105
+
106
+ # Set the engine
107
+ engine = _set_engine(engine)
108
+
109
+ # Fetch the known byte ranges needed to read
110
+ # `columns` and/or `row_groups`
111
+ data = _get_parquet_byte_ranges(
112
+ [path],
113
+ fs,
114
+ metadata=metadata,
115
+ columns=columns,
116
+ row_groups=row_groups,
117
+ engine=engine,
118
+ max_gap=max_gap,
119
+ max_block=max_block,
120
+ footer_sample_size=footer_sample_size,
121
+ )
122
+
123
+ # Extract file name from `data`
124
+ fn = next(iter(data)) if data else path
125
+
126
+ # Call self.open with "parts" caching
127
+ options = kwargs.pop("cache_options", {}).copy()
128
+ return fs.open(
129
+ fn,
130
+ mode=mode,
131
+ cache_type="parts",
132
+ cache_options={
133
+ **options,
134
+ **{
135
+ "data": data.get(fn, {}),
136
+ "strict": strict,
137
+ },
138
+ },
139
+ **kwargs,
140
+ )
141
+
142
+
143
+ def _get_parquet_byte_ranges(
144
+ paths,
145
+ fs,
146
+ metadata=None,
147
+ columns=None,
148
+ row_groups=None,
149
+ max_gap=64_000,
150
+ max_block=256_000_000,
151
+ footer_sample_size=1_000_000,
152
+ engine="auto",
153
+ ):
154
+ """Get a dictionary of the known byte ranges needed
155
+ to read a specific column/row-group selection from a
156
+ Parquet dataset. Each value in the output dictionary
157
+ is intended for use as the `data` argument for the
158
+ `KnownPartsOfAFile` caching strategy of a single path.
159
+ """
160
+
161
+ # Set engine if necessary
162
+ if isinstance(engine, str):
163
+ engine = _set_engine(engine)
164
+
165
+ # Pass to specialized function if metadata is defined
166
+ if metadata is not None:
167
+
168
+ # Use the provided parquet metadata object
169
+ # to avoid transferring/parsing footer metadata
170
+ return _get_parquet_byte_ranges_from_metadata(
171
+ metadata,
172
+ fs,
173
+ engine,
174
+ columns=columns,
175
+ row_groups=row_groups,
176
+ max_gap=max_gap,
177
+ max_block=max_block,
178
+ )
179
+
180
+ # Get file sizes asynchronously
181
+ file_sizes = fs.sizes(paths)
182
+
183
+ # Populate global paths, starts, & ends
184
+ result = {}
185
+ data_paths = []
186
+ data_starts = []
187
+ data_ends = []
188
+ add_header_magic = True
189
+ if columns is None and row_groups is None:
190
+ # We are NOT selecting specific columns or row-groups.
191
+ #
192
+ # We can avoid sampling the footers, and just transfer
193
+ # all file data with cat_ranges
194
+ for i, path in enumerate(paths):
195
+ result[path] = {}
196
+ for b in range(0, file_sizes[i], max_block):
197
+ data_paths.append(path)
198
+ data_starts.append(b)
199
+ data_ends.append(min(b + max_block, file_sizes[i]))
200
+ add_header_magic = False # "Magic" should already be included
201
+ else:
202
+ # We ARE selecting specific columns or row-groups.
203
+ #
204
+ # Gather file footers.
205
+ # We just take the last `footer_sample_size` bytes of each
206
+ # file (or the entire file if it is smaller than that)
207
+ footer_starts = []
208
+ footer_ends = []
209
+ for i, path in enumerate(paths):
210
+ footer_ends.append(file_sizes[i])
211
+ sample_size = max(0, file_sizes[i] - footer_sample_size)
212
+ footer_starts.append(sample_size)
213
+ footer_samples = fs.cat_ranges(paths, footer_starts, footer_ends)
214
+
215
+ # Check our footer samples and re-sample if necessary.
216
+ missing_footer_starts = footer_starts.copy()
217
+ large_footer = 0
218
+ for i, path in enumerate(paths):
219
+ footer_size = int.from_bytes(footer_samples[i][-8:-4], "little")
220
+ real_footer_start = file_sizes[i] - (footer_size + 8)
221
+ if real_footer_start < footer_starts[i]:
222
+ missing_footer_starts[i] = real_footer_start
223
+ large_footer = max(large_footer, (footer_size + 8))
224
+ if large_footer:
225
+ warnings.warn(
226
+ f"Not enough data was used to sample the parquet footer. "
227
+ f"Try setting footer_sample_size >= {large_footer}."
228
+ )
229
+ for i, block in enumerate(
230
+ fs.cat_ranges(
231
+ paths,
232
+ missing_footer_starts,
233
+ footer_starts,
234
+ )
235
+ ):
236
+ footer_samples[i] = block + footer_samples[i]
237
+ footer_starts[i] = missing_footer_starts[i]
238
+
239
+ # Calculate required byte ranges for each path
240
+ for i, path in enumerate(paths):
241
+
242
+ # Deal with small-file case.
243
+ # Just include all remaining bytes of the file
244
+ # in a single range.
245
+ if file_sizes[i] < max_block:
246
+ if footer_starts[i] > 0:
247
+ # Only need to transfer the data if the
248
+ # footer sample isn't already the whole file
249
+ data_paths.append(path)
250
+ data_starts.append(0)
251
+ data_ends.append(footer_starts[i])
252
+ continue
253
+
254
+ # Use "engine" to collect data byte ranges
255
+ path_data_starts, path_data_ends = engine._parquet_byte_ranges(
256
+ columns,
257
+ row_groups=row_groups,
258
+ footer=footer_samples[i],
259
+ footer_start=footer_starts[i],
260
+ )
261
+
262
+ data_paths += [path] * len(path_data_starts)
263
+ data_starts += path_data_starts
264
+ data_ends += path_data_ends
265
+
266
+ # Merge adjacent offset ranges
267
+ data_paths, data_starts, data_ends = merge_offset_ranges(
268
+ data_paths,
269
+ data_starts,
270
+ data_ends,
271
+ max_gap=max_gap,
272
+ max_block=max_block,
273
+ sort=False, # Should already be sorted
274
+ )
275
+
276
+ # Start by populating `result` with footer samples
277
+ for i, path in enumerate(paths):
278
+ result[path] = {(footer_starts[i], footer_ends[i]): footer_samples[i]}
279
+
280
+ # Transfer the data byte-ranges into local memory
281
+ _transfer_ranges(fs, result, data_paths, data_starts, data_ends)
282
+
283
+ # Add b"PAR1" to header if necessary
284
+ if add_header_magic:
285
+ _add_header_magic(result)
286
+
287
+ return result
288
+
289
+
290
+ def _get_parquet_byte_ranges_from_metadata(
291
+ metadata,
292
+ fs,
293
+ engine,
294
+ columns=None,
295
+ row_groups=None,
296
+ max_gap=64_000,
297
+ max_block=256_000_000,
298
+ ):
299
+ """Simplified version of `_get_parquet_byte_ranges` for
300
+ the case that an engine-specific `metadata` object is
301
+ provided, and the remote footer metadata does not need to
302
+ be transferred before calculating the required byte ranges.
303
+ """
304
+
305
+ # Use "engine" to collect data byte ranges
306
+ data_paths, data_starts, data_ends = engine._parquet_byte_ranges(
307
+ columns,
308
+ row_groups=row_groups,
309
+ metadata=metadata,
310
+ )
311
+
312
+ # Merge adjacent offset ranges
313
+ data_paths, data_starts, data_ends = merge_offset_ranges(
314
+ data_paths,
315
+ data_starts,
316
+ data_ends,
317
+ max_gap=max_gap,
318
+ max_block=max_block,
319
+ sort=False, # Should be sorted
320
+ )
321
+
322
+ # Transfer the data byte-ranges into local memory
323
+ result = {fn: {} for fn in list(set(data_paths))}
324
+ _transfer_ranges(fs, result, data_paths, data_starts, data_ends)
325
+
326
+ # Add b"PAR1" to header
327
+ _add_header_magic(result)
328
+
329
+ return result
330
+
331
+
332
+ def _transfer_ranges(fs, blocks, paths, starts, ends):
333
+ # Use cat_ranges to gather the data byte_ranges
334
+ ranges = (paths, starts, ends)
335
+ for path, start, stop, data in zip(*ranges, fs.cat_ranges(*ranges)):
336
+ blocks[path][(start, stop)] = data
337
+
338
+
339
+ def _add_header_magic(data):
340
+ # Add b"PAR1" to file headers
341
+ for i, path in enumerate(list(data.keys())):
342
+ add_magic = True
343
+ for k in data[path].keys():
344
+ if k[0] == 0 and k[1] >= 4:
345
+ add_magic = False
346
+ break
347
+ if add_magic:
348
+ data[path][(0, 4)] = b"PAR1"
349
+
350
+
351
+ def _set_engine(engine_str):
352
+
353
+ # Define a list of parquet engines to try
354
+ if engine_str == "auto":
355
+ try_engines = ("fastparquet", "pyarrow")
356
+ elif not isinstance(engine_str, str):
357
+ raise ValueError(
358
+ "Failed to set parquet engine! "
359
+ "Please pass 'fastparquet', 'pyarrow', or 'auto'"
360
+ )
361
+ elif engine_str not in ("fastparquet", "pyarrow"):
362
+ raise ValueError(f"{engine_str} engine not supported by `fsspec.parquet`")
363
+ else:
364
+ try_engines = [engine_str]
365
+
366
+ # Try importing the engines in `try_engines`,
367
+ # and choose the first one that succeeds
368
+ for engine in try_engines:
369
+ try:
370
+ if engine == "fastparquet":
371
+ return FastparquetEngine()
372
+ elif engine == "pyarrow":
373
+ return PyarrowEngine()
374
+ except ImportError:
375
+ pass
376
+
377
+ # Raise an error if a supported parquet engine
378
+ # was not found
379
+ raise ImportError(
380
+ f"The following parquet engines are not installed "
381
+ f"in your python environment: {try_engines}."
382
+ f"Please install 'fastparquert' or 'pyarrow' to "
383
+ f"utilize the `fsspec.parquet` module."
384
+ )
385
+
386
+
387
+ class FastparquetEngine:
388
+
389
+ # The purpose of the FastparquetEngine class is
390
+ # to check if fastparquet can be imported (on initialization)
391
+ # and to define a `_parquet_byte_ranges` method. In the
392
+ # future, this class may also be used to define other
393
+ # methods/logic that are specific to fastparquet.
394
+
395
+ def __init__(self):
396
+ import fastparquet as fp
397
+
398
+ self.fp = fp
399
+
400
+ def _row_group_filename(self, row_group, pf):
401
+ return pf.row_group_filename(row_group)
402
+
403
+ def _parquet_byte_ranges(
404
+ self,
405
+ columns,
406
+ row_groups=None,
407
+ metadata=None,
408
+ footer=None,
409
+ footer_start=None,
410
+ ):
411
+
412
+ # Initialize offset ranges and define ParqetFile metadata
413
+ pf = metadata
414
+ data_paths, data_starts, data_ends = [], [], []
415
+ if pf is None:
416
+ pf = self.fp.ParquetFile(io.BytesIO(footer))
417
+
418
+ # Convert columns to a set and add any index columns
419
+ # specified in the pandas metadata (just in case)
420
+ column_set = None if columns is None else set(columns)
421
+ if column_set is not None and hasattr(pf, "pandas_metadata"):
422
+ md_index = [
423
+ ind
424
+ for ind in pf.pandas_metadata.get("index_columns", [])
425
+ # Ignore RangeIndex information
426
+ if not isinstance(ind, dict)
427
+ ]
428
+ column_set |= set(md_index)
429
+
430
+ # Check if row_groups is a list of integers
431
+ # or a list of row-group metadata
432
+ if row_groups and not isinstance(row_groups[0], int):
433
+ # Input row_groups contains row-group metadata
434
+ row_group_indices = None
435
+ else:
436
+ # Input row_groups contains row-group indices
437
+ row_group_indices = row_groups
438
+ row_groups = pf.row_groups
439
+
440
+ # Loop through column chunks to add required byte ranges
441
+ for r, row_group in enumerate(row_groups):
442
+ # Skip this row-group if we are targeting
443
+ # specific row-groups
444
+ if row_group_indices is None or r in row_group_indices:
445
+
446
+ # Find the target parquet-file path for `row_group`
447
+ fn = self._row_group_filename(row_group, pf)
448
+
449
+ for column in row_group.columns:
450
+ name = column.meta_data.path_in_schema[0]
451
+ # Skip this column if we are targeting a
452
+ # specific columns
453
+ if column_set is None or name in column_set:
454
+ file_offset0 = column.meta_data.dictionary_page_offset
455
+ if file_offset0 is None:
456
+ file_offset0 = column.meta_data.data_page_offset
457
+ num_bytes = column.meta_data.total_compressed_size
458
+ if footer_start is None or file_offset0 < footer_start:
459
+ data_paths.append(fn)
460
+ data_starts.append(file_offset0)
461
+ data_ends.append(
462
+ min(
463
+ file_offset0 + num_bytes,
464
+ footer_start or (file_offset0 + num_bytes),
465
+ )
466
+ )
467
+
468
+ if metadata:
469
+ # The metadata in this call may map to multiple
470
+ # file paths. Need to include `data_paths`
471
+ return data_paths, data_starts, data_ends
472
+ return data_starts, data_ends
473
+
474
+
475
+ class PyarrowEngine:
476
+
477
+ # The purpose of the PyarrowEngine class is
478
+ # to check if pyarrow can be imported (on initialization)
479
+ # and to define a `_parquet_byte_ranges` method. In the
480
+ # future, this class may also be used to define other
481
+ # methods/logic that are specific to pyarrow.
482
+
483
+ def __init__(self):
484
+ import pyarrow.parquet as pq
485
+
486
+ self.pq = pq
487
+
488
+ def _row_group_filename(self, row_group, metadata):
489
+ raise NotImplementedError
490
+
491
+ def _parquet_byte_ranges(
492
+ self,
493
+ columns,
494
+ row_groups=None,
495
+ metadata=None,
496
+ footer=None,
497
+ footer_start=None,
498
+ ):
499
+
500
+ if metadata is not None:
501
+ raise ValueError("metadata input not supported for PyarrowEngine")
502
+
503
+ data_starts, data_ends = [], []
504
+ md = self.pq.ParquetFile(io.BytesIO(footer)).metadata
505
+
506
+ # Convert columns to a set and add any index columns
507
+ # specified in the pandas metadata (just in case)
508
+ column_set = None if columns is None else set(columns)
509
+ if column_set is not None:
510
+ schema = md.schema.to_arrow_schema()
511
+ has_pandas_metadata = (
512
+ schema.metadata is not None and b"pandas" in schema.metadata
513
+ )
514
+ if has_pandas_metadata:
515
+ md_index = [
516
+ ind
517
+ for ind in json.loads(
518
+ schema.metadata[b"pandas"].decode("utf8")
519
+ ).get("index_columns", [])
520
+ # Ignore RangeIndex information
521
+ if not isinstance(ind, dict)
522
+ ]
523
+ column_set |= set(md_index)
524
+
525
+ # Loop through column chunks to add required byte ranges
526
+ for r in range(md.num_row_groups):
527
+ # Skip this row-group if we are targeting
528
+ # specific row-groups
529
+ if row_groups is None or r in row_groups:
530
+ row_group = md.row_group(r)
531
+ for c in range(row_group.num_columns):
532
+ column = row_group.column(c)
533
+ name = column.path_in_schema
534
+ # Skip this column if we are targeting a
535
+ # specific columns
536
+ split_name = name.split(".")[0]
537
+ if (
538
+ column_set is None
539
+ or name in column_set
540
+ or split_name in column_set
541
+ ):
542
+ file_offset0 = column.dictionary_page_offset
543
+ if file_offset0 is None:
544
+ file_offset0 = column.data_page_offset
545
+ num_bytes = column.total_compressed_size
546
+ if file_offset0 < footer_start:
547
+ data_starts.append(file_offset0)
548
+ data_ends.append(
549
+ min(file_offset0 + num_bytes, footer_start)
550
+ )
551
+ return data_starts, data_ends
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/registry.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib
2
+
3
+ __all__ = ["registry", "get_filesystem_class", "default"]
4
+
5
+ # mapping protocol: implementation class object
6
+ _registry = {} # internal, mutable
7
+
8
+
9
+ class ReadOnlyError(TypeError):
10
+ pass
11
+
12
+
13
+ class ReadOnlyRegistry(dict):
14
+ """Dict-like registry, but immutable
15
+
16
+ Maps backend name to implementation class
17
+
18
+ To add backend implementations, use ``register_implementation``
19
+ """
20
+
21
+ def __init__(self, target):
22
+ self.target = target
23
+
24
+ def __getitem__(self, item):
25
+ return self.target[item]
26
+
27
+ def __delitem__(self, key):
28
+ raise ReadOnlyError
29
+
30
+ def __setitem__(self, key, value):
31
+ raise ReadOnlyError
32
+
33
+ def clear(self):
34
+ raise ReadOnlyError
35
+
36
+ def __contains__(self, item):
37
+ return item in self.target
38
+
39
+ def __iter__(self):
40
+ yield from self.target
41
+
42
+
43
+ def register_implementation(name, cls, clobber=True, errtxt=None):
44
+ """Add implementation class to the registry
45
+
46
+ Parameters
47
+ ----------
48
+ name: str
49
+ Protocol name to associate with the class
50
+ cls: class or str
51
+ if a class: fsspec-compliant implementation class (normally inherits from
52
+ ``fsspec.AbstractFileSystem``, gets added straight to the registry. If a
53
+ str, the full path to an implementation class like package.module.class,
54
+ which gets added to known_implementations,
55
+ so the import is deferred until the filesystem is actually used.
56
+ clobber: bool (optional)
57
+ Whether to overwrite a protocol with the same name; if False, will raise
58
+ instead.
59
+ errtxt: str (optional)
60
+ If given, then a failure to import the given class will result in this
61
+ text being given.
62
+ """
63
+ if isinstance(cls, str):
64
+ if name in known_implementations and clobber is False:
65
+ raise ValueError(
66
+ "Name (%s) already in the known_implementations and clobber "
67
+ "is False" % name
68
+ )
69
+ known_implementations[name] = {
70
+ "class": cls,
71
+ "err": errtxt or "%s import failed for protocol %s" % (cls, name),
72
+ }
73
+
74
+ else:
75
+ if name in registry and clobber is False:
76
+ raise ValueError(
77
+ "Name (%s) already in the registry and clobber is False" % name
78
+ )
79
+ _registry[name] = cls
80
+
81
+
82
+ registry = ReadOnlyRegistry(_registry)
83
+ default = "file"
84
+
85
+ # protocols mapped to the class which implements them. This dict can
86
+ # updated with register_implementation
87
+ known_implementations = {
88
+ "file": {"class": "fsspec.implementations.local.LocalFileSystem"},
89
+ "memory": {"class": "fsspec.implementations.memory.MemoryFileSystem"},
90
+ "dropbox": {
91
+ "class": "dropboxdrivefs.DropboxDriveFileSystem",
92
+ "err": (
93
+ 'DropboxFileSystem requires "dropboxdrivefs",'
94
+ '"requests" and "dropbox" to be installed'
95
+ ),
96
+ },
97
+ "http": {
98
+ "class": "fsspec.implementations.http.HTTPFileSystem",
99
+ "err": 'HTTPFileSystem requires "requests" and "aiohttp" to be installed',
100
+ },
101
+ "https": {
102
+ "class": "fsspec.implementations.http.HTTPFileSystem",
103
+ "err": 'HTTPFileSystem requires "requests" and "aiohttp" to be installed',
104
+ },
105
+ "zip": {"class": "fsspec.implementations.zip.ZipFileSystem"},
106
+ "tar": {"class": "fsspec.implementations.tar.TarFileSystem"},
107
+ "gcs": {
108
+ "class": "gcsfs.GCSFileSystem",
109
+ "err": "Please install gcsfs to access Google Storage",
110
+ },
111
+ "gs": {
112
+ "class": "gcsfs.GCSFileSystem",
113
+ "err": "Please install gcsfs to access Google Storage",
114
+ },
115
+ "gdrive": {
116
+ "class": "gdrivefs.GoogleDriveFileSystem",
117
+ "err": "Please install gdrivefs for access to Google Drive",
118
+ },
119
+ "sftp": {
120
+ "class": "fsspec.implementations.sftp.SFTPFileSystem",
121
+ "err": 'SFTPFileSystem requires "paramiko" to be installed',
122
+ },
123
+ "ssh": {
124
+ "class": "fsspec.implementations.sftp.SFTPFileSystem",
125
+ "err": 'SFTPFileSystem requires "paramiko" to be installed',
126
+ },
127
+ "ftp": {"class": "fsspec.implementations.ftp.FTPFileSystem"},
128
+ "hdfs": {
129
+ "class": "fsspec.implementations.hdfs.PyArrowHDFS",
130
+ "err": "pyarrow and local java libraries required for HDFS",
131
+ },
132
+ "arrow_hdfs": {
133
+ "class": "fsspec.implementations.arrow.HadoopFileSystem",
134
+ "err": "pyarrow and local java libraries required for HDFS",
135
+ },
136
+ "webhdfs": {
137
+ "class": "fsspec.implementations.webhdfs.WebHDFS",
138
+ "err": 'webHDFS access requires "requests" to be installed',
139
+ },
140
+ "s3": {"class": "s3fs.S3FileSystem", "err": "Install s3fs to access S3"},
141
+ "s3a": {"class": "s3fs.S3FileSystem", "err": "Install s3fs to access S3"},
142
+ "wandb": {"class": "wandbfs.WandbFS", "err": "Install wandbfs to access wandb"},
143
+ "oci": {
144
+ "class": "ocifs.OCIFileSystem",
145
+ "err": "Install ocifs to access OCI Object Storage",
146
+ },
147
+ "adl": {
148
+ "class": "adlfs.AzureDatalakeFileSystem",
149
+ "err": "Install adlfs to access Azure Datalake Gen1",
150
+ },
151
+ "abfs": {
152
+ "class": "adlfs.AzureBlobFileSystem",
153
+ "err": "Install adlfs to access Azure Datalake Gen2 and Azure Blob Storage",
154
+ },
155
+ "az": {
156
+ "class": "adlfs.AzureBlobFileSystem",
157
+ "err": "Install adlfs to access Azure Datalake Gen2 and Azure Blob Storage",
158
+ },
159
+ "cached": {"class": "fsspec.implementations.cached.CachingFileSystem"},
160
+ "blockcache": {"class": "fsspec.implementations.cached.CachingFileSystem"},
161
+ "filecache": {"class": "fsspec.implementations.cached.WholeFileCacheFileSystem"},
162
+ "simplecache": {"class": "fsspec.implementations.cached.SimpleCacheFileSystem"},
163
+ "dask": {
164
+ "class": "fsspec.implementations.dask.DaskWorkerFileSystem",
165
+ "err": "Install dask distributed to access worker file system",
166
+ },
167
+ "dbfs": {
168
+ "class": "fsspec.implementations.dbfs.DatabricksFileSystem",
169
+ "err": "Install the requests package to use the DatabricksFileSystem",
170
+ },
171
+ "github": {
172
+ "class": "fsspec.implementations.github.GithubFileSystem",
173
+ "err": "Install the requests package to use the github FS",
174
+ },
175
+ "git": {
176
+ "class": "fsspec.implementations.git.GitFileSystem",
177
+ "err": "Install pygit2 to browse local git repos",
178
+ },
179
+ "smb": {
180
+ "class": "fsspec.implementations.smb.SMBFileSystem",
181
+ "err": 'SMB requires "smbprotocol" or "smbprotocol[kerberos]" installed',
182
+ },
183
+ "jupyter": {
184
+ "class": "fsspec.implementations.jupyter.JupyterFileSystem",
185
+ "err": "Jupyter FS requires requests to be installed",
186
+ },
187
+ "jlab": {
188
+ "class": "fsspec.implementations.jupyter.JupyterFileSystem",
189
+ "err": "Jupyter FS requires requests to be installed",
190
+ },
191
+ "libarchive": {
192
+ "class": "fsspec.implementations.libarchive.LibArchiveFileSystem",
193
+ "err": "LibArchive requires to be installed",
194
+ },
195
+ "reference": {"class": "fsspec.implementations.reference.ReferenceFileSystem"},
196
+ "generic": {"class": "fsspec.generic.GenericFileSystem"},
197
+ "oss": {
198
+ "class": "ossfs.OSSFileSystem",
199
+ "err": "Install ossfs to access Alibaba Object Storage System",
200
+ },
201
+ "webdav": {
202
+ "class": "webdav4.fsspec.WebdavFileSystem",
203
+ "err": "Install webdav4 to access WebDAV",
204
+ },
205
+ }
206
+
207
+
208
+ def get_filesystem_class(protocol):
209
+ """Fetch named protocol implementation from the registry
210
+
211
+ The dict ``known_implementations`` maps protocol names to the locations
212
+ of classes implementing the corresponding file-system. When used for the
213
+ first time, appropriate imports will happen and the class will be placed in
214
+ the registry. All subsequent calls will fetch directly from the registry.
215
+
216
+ Some protocol implementations require additional dependencies, and so the
217
+ import may fail. In this case, the string in the "err" field of the
218
+ ``known_implementations`` will be given as the error message.
219
+ """
220
+ if not protocol:
221
+ protocol = default
222
+
223
+ if protocol not in registry:
224
+ if protocol not in known_implementations:
225
+ raise ValueError("Protocol not known: %s" % protocol)
226
+ bit = known_implementations[protocol]
227
+ try:
228
+ register_implementation(protocol, _import_class(bit["class"]))
229
+ except ImportError as e:
230
+ raise ImportError(bit["err"]) from e
231
+ cls = registry[protocol]
232
+ if getattr(cls, "protocol", None) in ("abstract", None):
233
+ cls.protocol = protocol
234
+
235
+ return cls
236
+
237
+
238
+ def _import_class(cls, minv=None):
239
+ """Take a string FQP and return the imported class or identifier
240
+
241
+ clas is of the form "package.module.klass" or "package.module:subobject.klass"
242
+ """
243
+ if ":" in cls:
244
+ mod, name = cls.rsplit(":", 1)
245
+ mod = importlib.import_module(mod)
246
+ for part in name.split("."):
247
+ mod = getattr(mod, part)
248
+ return mod
249
+ else:
250
+ mod, name = cls.rsplit(".", 1)
251
+ mod = importlib.import_module(mod)
252
+ return getattr(mod, name)
253
+
254
+
255
+ def filesystem(protocol, **storage_options):
256
+ """Instantiate filesystems for given protocol and arguments
257
+
258
+ ``storage_options`` are specific to the protocol being chosen, and are
259
+ passed directly to the class.
260
+ """
261
+ cls = get_filesystem_class(protocol)
262
+ return cls(**storage_options)
263
+
264
+
265
+ def available_protocols():
266
+ """Return a list of the implemented protocols.
267
+
268
+ Note that any given protocol may require extra packages to be importable.
269
+ """
270
+ return list(known_implementations)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/spec.py ADDED
@@ -0,0 +1,1697 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import logging
3
+ import os
4
+ import threading
5
+ import warnings
6
+ import weakref
7
+ from errno import ESPIPE
8
+ from glob import has_magic
9
+ from hashlib import sha256
10
+
11
+ from .callbacks import _DEFAULT_CALLBACK
12
+ from .config import apply_config, conf
13
+ from .dircache import DirCache
14
+ from .transaction import Transaction
15
+ from .utils import (
16
+ _unstrip_protocol,
17
+ isfilelike,
18
+ other_paths,
19
+ read_block,
20
+ stringify_path,
21
+ tokenize,
22
+ )
23
+
24
+ logger = logging.getLogger("fsspec")
25
+
26
+
27
+ def make_instance(cls, args, kwargs):
28
+ return cls(*args, **kwargs)
29
+
30
+
31
+ class _Cached(type):
32
+ """
33
+ Metaclass for caching file system instances.
34
+
35
+ Notes
36
+ -----
37
+ Instances are cached according to
38
+
39
+ * The values of the class attributes listed in `_extra_tokenize_attributes`
40
+ * The arguments passed to ``__init__``.
41
+
42
+ This creates an additional reference to the filesystem, which prevents the
43
+ filesystem from being garbage collected when all *user* references go away.
44
+ A call to the :meth:`AbstractFileSystem.clear_instance_cache` must *also*
45
+ be made for a filesystem instance to be garbage collected.
46
+ """
47
+
48
+ def __init__(cls, *args, **kwargs):
49
+ super().__init__(*args, **kwargs)
50
+ # Note: we intentionally create a reference here, to avoid garbage
51
+ # collecting instances when all other references are gone. To really
52
+ # delete a FileSystem, the cache must be cleared.
53
+ if conf.get("weakref_instance_cache"): # pragma: no cover
54
+ # debug option for analysing fork/spawn conditions
55
+ cls._cache = weakref.WeakValueDictionary()
56
+ else:
57
+ cls._cache = {}
58
+ cls._pid = os.getpid()
59
+
60
+ def __call__(cls, *args, **kwargs):
61
+ kwargs = apply_config(cls, kwargs)
62
+ extra_tokens = tuple(
63
+ getattr(cls, attr, None) for attr in cls._extra_tokenize_attributes
64
+ )
65
+ token = tokenize(
66
+ cls, cls._pid, threading.get_ident(), *args, *extra_tokens, **kwargs
67
+ )
68
+ skip = kwargs.pop("skip_instance_cache", False)
69
+ if os.getpid() != cls._pid:
70
+ cls._cache.clear()
71
+ cls._pid = os.getpid()
72
+ if not skip and cls.cachable and token in cls._cache:
73
+ cls._latest = token
74
+ return cls._cache[token]
75
+ else:
76
+ obj = super().__call__(*args, **kwargs)
77
+ # Setting _fs_token here causes some static linters to complain.
78
+ obj._fs_token_ = token
79
+ obj.storage_args = args
80
+ obj.storage_options = kwargs
81
+ if obj.async_impl:
82
+ from .asyn import mirror_sync_methods
83
+
84
+ mirror_sync_methods(obj)
85
+
86
+ if cls.cachable and not skip:
87
+ cls._latest = token
88
+ cls._cache[token] = obj
89
+ return obj
90
+
91
+
92
+ class AbstractFileSystem(metaclass=_Cached):
93
+ """
94
+ An abstract super-class for pythonic file-systems
95
+
96
+ Implementations are expected to be compatible with or, better, subclass
97
+ from here.
98
+ """
99
+
100
+ cachable = True # this class can be cached, instances reused
101
+ _cached = False
102
+ blocksize = 2**22
103
+ sep = "/"
104
+ protocol = "abstract"
105
+ _latest = None
106
+ async_impl = False
107
+ root_marker = "" # For some FSs, may require leading '/' or other character
108
+
109
+ #: Extra *class attributes* that should be considered when hashing.
110
+ _extra_tokenize_attributes = ()
111
+
112
+ def __init__(self, *args, **storage_options):
113
+ """Create and configure file-system instance
114
+
115
+ Instances may be cachable, so if similar enough arguments are seen
116
+ a new instance is not required. The token attribute exists to allow
117
+ implementations to cache instances if they wish.
118
+
119
+ A reasonable default should be provided if there are no arguments.
120
+
121
+ Subclasses should call this method.
122
+
123
+ Parameters
124
+ ----------
125
+ use_listings_cache, listings_expiry_time, max_paths:
126
+ passed to ``DirCache``, if the implementation supports
127
+ directory listing caching. Pass use_listings_cache=False
128
+ to disable such caching.
129
+ skip_instance_cache: bool
130
+ If this is a cachable implementation, pass True here to force
131
+ creating a new instance even if a matching instance exists, and prevent
132
+ storing this instance.
133
+ asynchronous: bool
134
+ loop: asyncio-compatible IOLoop or None
135
+ """
136
+ if self._cached:
137
+ # reusing instance, don't change
138
+ return
139
+ self._cached = True
140
+ self._intrans = False
141
+ self._transaction = None
142
+ self._invalidated_caches_in_transaction = []
143
+ self.dircache = DirCache(**storage_options)
144
+
145
+ if storage_options.pop("add_docs", None):
146
+ warnings.warn("add_docs is no longer supported.", FutureWarning)
147
+
148
+ if storage_options.pop("add_aliases", None):
149
+ warnings.warn("add_aliases has been removed.", FutureWarning)
150
+ # This is set in _Cached
151
+ self._fs_token_ = None
152
+
153
+ @property
154
+ def _fs_token(self):
155
+ return self._fs_token_
156
+
157
+ def __dask_tokenize__(self):
158
+ return self._fs_token
159
+
160
+ def __hash__(self):
161
+ return int(self._fs_token, 16)
162
+
163
+ def __eq__(self, other):
164
+ return isinstance(other, type(self)) and self._fs_token == other._fs_token
165
+
166
+ def __reduce__(self):
167
+ return make_instance, (type(self), self.storage_args, self.storage_options)
168
+
169
+ @classmethod
170
+ def _strip_protocol(cls, path):
171
+ """Turn path from fully-qualified to file-system-specific
172
+
173
+ May require FS-specific handling, e.g., for relative paths or links.
174
+ """
175
+ if isinstance(path, list):
176
+ return [cls._strip_protocol(p) for p in path]
177
+ path = stringify_path(path)
178
+ protos = (cls.protocol,) if isinstance(cls.protocol, str) else cls.protocol
179
+ for protocol in protos:
180
+ if path.startswith(protocol + "://"):
181
+ path = path[len(protocol) + 3 :]
182
+ elif path.startswith(protocol + "::"):
183
+ path = path[len(protocol) + 2 :]
184
+ path = path.rstrip("/")
185
+ # use of root_marker to make minimum required path, e.g., "/"
186
+ return path or cls.root_marker
187
+
188
+ def unstrip_protocol(self, name):
189
+ """Format FS-specific path to generic, including protocol"""
190
+ if isinstance(self.protocol, str):
191
+ if name.startswith(self.protocol):
192
+ return name
193
+ return self.protocol + "://" + name
194
+ else:
195
+ if name.startswith(tuple(self.protocol)):
196
+ return name
197
+ return self.protocol[0] + "://" + name
198
+
199
+ @staticmethod
200
+ def _get_kwargs_from_urls(path):
201
+ """If kwargs can be encoded in the paths, extract them here
202
+
203
+ This should happen before instantiation of the class; incoming paths
204
+ then should be amended to strip the options in methods.
205
+
206
+ Examples may look like an sftp path "sftp://user@host:/my/path", where
207
+ the user and host should become kwargs and later get stripped.
208
+ """
209
+ # by default, nothing happens
210
+ return {}
211
+
212
+ @classmethod
213
+ def current(cls):
214
+ """Return the most recently instantiated FileSystem
215
+
216
+ If no instance has been created, then create one with defaults
217
+ """
218
+ if cls._latest in cls._cache:
219
+ return cls._cache[cls._latest]
220
+ return cls()
221
+
222
+ @property
223
+ def transaction(self):
224
+ """A context within which files are committed together upon exit
225
+
226
+ Requires the file class to implement `.commit()` and `.discard()`
227
+ for the normal and exception cases.
228
+ """
229
+ if self._transaction is None:
230
+ self._transaction = Transaction(self)
231
+ return self._transaction
232
+
233
+ def start_transaction(self):
234
+ """Begin write transaction for deferring files, non-context version"""
235
+ self._intrans = True
236
+ self._transaction = Transaction(self)
237
+ return self.transaction
238
+
239
+ def end_transaction(self):
240
+ """Finish write transaction, non-context version"""
241
+ self.transaction.complete()
242
+ self._transaction = None
243
+ # The invalid cache must be cleared after the transcation is completed.
244
+ for path in self._invalidated_caches_in_transaction:
245
+ self.invalidate_cache(path)
246
+ self._invalidated_caches_in_transaction.clear()
247
+
248
+ def invalidate_cache(self, path=None):
249
+ """
250
+ Discard any cached directory information
251
+
252
+ Parameters
253
+ ----------
254
+ path: string or None
255
+ If None, clear all listings cached else listings at or under given
256
+ path.
257
+ """
258
+ # Not necessary to implement invalidation mechanism, may have no cache.
259
+ # But if have, you should call this method of parent class from your
260
+ # subclass to ensure expiring caches after transacations correctly.
261
+ # See the implementation of FTPFileSystem in ftp.py
262
+ if self._intrans:
263
+ self._invalidated_caches_in_transaction.append(path)
264
+
265
+ def mkdir(self, path, create_parents=True, **kwargs):
266
+ """
267
+ Create directory entry at path
268
+
269
+ For systems that don't have true directories, may create an for
270
+ this instance only and not touch the real filesystem
271
+
272
+ Parameters
273
+ ----------
274
+ path: str
275
+ location
276
+ create_parents: bool
277
+ if True, this is equivalent to ``makedirs``
278
+ kwargs:
279
+ may be permissions, etc.
280
+ """
281
+ pass # not necessary to implement, may not have directories
282
+
283
+ def makedirs(self, path, exist_ok=False):
284
+ """Recursively make directories
285
+
286
+ Creates directory at path and any intervening required directories.
287
+ Raises exception if, for instance, the path already exists but is a
288
+ file.
289
+
290
+ Parameters
291
+ ----------
292
+ path: str
293
+ leaf directory name
294
+ exist_ok: bool (False)
295
+ If False, will error if the target already exists
296
+ """
297
+ pass # not necessary to implement, may not have directories
298
+
299
+ def rmdir(self, path):
300
+ """Remove a directory, if empty"""
301
+ pass # not necessary to implement, may not have directories
302
+
303
+ def ls(self, path, detail=True, **kwargs):
304
+ """List objects at path.
305
+
306
+ This should include subdirectories and files at that location. The
307
+ difference between a file and a directory must be clear when details
308
+ are requested.
309
+
310
+ The specific keys, or perhaps a FileInfo class, or similar, is TBD,
311
+ but must be consistent across implementations.
312
+ Must include:
313
+
314
+ - full path to the entry (without protocol)
315
+ - size of the entry, in bytes. If the value cannot be determined, will
316
+ be ``None``.
317
+ - type of entry, "file", "directory" or other
318
+
319
+ Additional information
320
+ may be present, aproriate to the file-system, e.g., generation,
321
+ checksum, etc.
322
+
323
+ May use refresh=True|False to allow use of self._ls_from_cache to
324
+ check for a saved listing and avoid calling the backend. This would be
325
+ common where listing may be expensive.
326
+
327
+ Parameters
328
+ ----------
329
+ path: str
330
+ detail: bool
331
+ if True, gives a list of dictionaries, where each is the same as
332
+ the result of ``info(path)``. If False, gives a list of paths
333
+ (str).
334
+ kwargs: may have additional backend-specific options, such as version
335
+ information
336
+
337
+ Returns
338
+ -------
339
+ List of strings if detail is False, or list of directory information
340
+ dicts if detail is True.
341
+ """
342
+ raise NotImplementedError
343
+
344
+ def _ls_from_cache(self, path):
345
+ """Check cache for listing
346
+
347
+ Returns listing, if found (may me empty list for a directly that exists
348
+ but contains nothing), None if not in cache.
349
+ """
350
+ parent = self._parent(path)
351
+ if path.rstrip("/") in self.dircache:
352
+ return self.dircache[path.rstrip("/")]
353
+ try:
354
+ files = [
355
+ f
356
+ for f in self.dircache[parent]
357
+ if f["name"] == path
358
+ or (f["name"] == path.rstrip("/") and f["type"] == "directory")
359
+ ]
360
+ if len(files) == 0:
361
+ # parent dir was listed but did not contain this file
362
+ raise FileNotFoundError(path)
363
+ return files
364
+ except KeyError:
365
+ pass
366
+
367
+ def walk(self, path, maxdepth=None, **kwargs):
368
+ """Return all files belows path
369
+
370
+ List all files, recursing into subdirectories; output is iterator-style,
371
+ like ``os.walk()``. For a simple list of files, ``find()`` is available.
372
+
373
+ Note that the "files" outputted will include anything that is not
374
+ a directory, such as links.
375
+
376
+ Parameters
377
+ ----------
378
+ path: str
379
+ Root to recurse into
380
+ maxdepth: int
381
+ Maximum recursion depth. None means limitless, but not recommended
382
+ on link-based file-systems.
383
+ kwargs: passed to ``ls``
384
+ """
385
+ path = self._strip_protocol(path)
386
+ full_dirs = {}
387
+ dirs = {}
388
+ files = {}
389
+
390
+ detail = kwargs.pop("detail", False)
391
+ try:
392
+ listing = self.ls(path, detail=True, **kwargs)
393
+ except (FileNotFoundError, IOError):
394
+ if detail:
395
+ return path, {}, {}
396
+ return path, [], []
397
+
398
+ for info in listing:
399
+ # each info name must be at least [path]/part , but here
400
+ # we check also for names like [path]/part/
401
+ pathname = info["name"].rstrip("/")
402
+ name = pathname.rsplit("/", 1)[-1]
403
+ if info["type"] == "directory" and pathname != path:
404
+ # do not include "self" path
405
+ full_dirs[pathname] = info
406
+ dirs[name] = info
407
+ elif pathname == path:
408
+ # file-like with same name as give path
409
+ files[""] = info
410
+ else:
411
+ files[name] = info
412
+
413
+ if detail:
414
+ yield path, dirs, files
415
+ else:
416
+ yield path, list(dirs), list(files)
417
+
418
+ if maxdepth is not None:
419
+ maxdepth -= 1
420
+ if maxdepth < 1:
421
+ return
422
+
423
+ for d in full_dirs:
424
+ yield from self.walk(d, maxdepth=maxdepth, detail=detail, **kwargs)
425
+
426
+ def find(self, path, maxdepth=None, withdirs=False, detail=False, **kwargs):
427
+ """List all files below path.
428
+
429
+ Like posix ``find`` command without conditions
430
+
431
+ Parameters
432
+ ----------
433
+ path : str
434
+ maxdepth: int or None
435
+ If not None, the maximum number of levels to descend
436
+ withdirs: bool
437
+ Whether to include directory paths in the output. This is True
438
+ when used by glob, but users usually only want files.
439
+ kwargs are passed to ``ls``.
440
+ """
441
+ # TODO: allow equivalent of -name parameter
442
+ path = self._strip_protocol(path)
443
+ out = dict()
444
+ for _, dirs, files in self.walk(path, maxdepth, detail=True, **kwargs):
445
+ if withdirs:
446
+ files.update(dirs)
447
+ out.update({info["name"]: info for name, info in files.items()})
448
+ if not out and self.isfile(path):
449
+ # walk works on directories, but find should also return [path]
450
+ # when path happens to be a file
451
+ out[path] = {}
452
+ names = sorted(out)
453
+ if not detail:
454
+ return names
455
+ else:
456
+ return {name: out[name] for name in names}
457
+
458
+ def du(self, path, total=True, maxdepth=None, **kwargs):
459
+ """Space used by files within a path
460
+
461
+ Parameters
462
+ ----------
463
+ path: str
464
+ total: bool
465
+ whether to sum all the file sizes
466
+ maxdepth: int or None
467
+ maximum number of directory levels to descend, None for unlimited.
468
+ kwargs: passed to ``ls``
469
+
470
+ Returns
471
+ -------
472
+ Dict of {fn: size} if total=False, or int otherwise, where numbers
473
+ refer to bytes used.
474
+ """
475
+ sizes = {}
476
+ for f in self.find(path, maxdepth=maxdepth, **kwargs):
477
+ info = self.info(f)
478
+ sizes[info["name"]] = info["size"]
479
+ if total:
480
+ return sum(sizes.values())
481
+ else:
482
+ return sizes
483
+
484
+ def glob(self, path, **kwargs):
485
+ """
486
+ Find files by glob-matching.
487
+
488
+ If the path ends with '/' and does not contain "*", it is essentially
489
+ the same as ``ls(path)``, returning only files.
490
+
491
+ We support ``"**"``,
492
+ ``"?"`` and ``"[..]"``. We do not support ^ for pattern negation.
493
+
494
+ Search path names that contain embedded characters special to this
495
+ implementation of glob may not produce expected results;
496
+ e.g., 'foo/bar/*starredfilename*'.
497
+
498
+ kwargs are passed to ``ls``.
499
+ """
500
+ import re
501
+
502
+ ends = path.endswith("/")
503
+ path = self._strip_protocol(path)
504
+ indstar = path.find("*") if path.find("*") >= 0 else len(path)
505
+ indques = path.find("?") if path.find("?") >= 0 else len(path)
506
+ indbrace = path.find("[") if path.find("[") >= 0 else len(path)
507
+
508
+ ind = min(indstar, indques, indbrace)
509
+
510
+ detail = kwargs.pop("detail", False)
511
+
512
+ if not has_magic(path):
513
+ root = path
514
+ depth = 1
515
+ if ends:
516
+ path += "/*"
517
+ elif self.exists(path):
518
+ if not detail:
519
+ return [path]
520
+ else:
521
+ return {path: self.info(path)}
522
+ else:
523
+ if not detail:
524
+ return [] # glob of non-existent returns empty
525
+ else:
526
+ return {}
527
+ elif "/" in path[:ind]:
528
+ ind2 = path[:ind].rindex("/")
529
+ root = path[: ind2 + 1]
530
+ depth = None if "**" in path else path[ind2 + 1 :].count("/") + 1
531
+ else:
532
+ root = ""
533
+ depth = None if "**" in path else path[ind + 1 :].count("/") + 1
534
+
535
+ allpaths = self.find(root, maxdepth=depth, withdirs=True, detail=True, **kwargs)
536
+ # Escape characters special to python regex, leaving our supported
537
+ # special characters in place.
538
+ # See https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html
539
+ # for shell globbing details.
540
+ pattern = (
541
+ "^"
542
+ + (
543
+ path.replace("\\", r"\\")
544
+ .replace(".", r"\.")
545
+ .replace("+", r"\+")
546
+ .replace("//", "/")
547
+ .replace("(", r"\(")
548
+ .replace(")", r"\)")
549
+ .replace("|", r"\|")
550
+ .replace("^", r"\^")
551
+ .replace("$", r"\$")
552
+ .replace("{", r"\{")
553
+ .replace("}", r"\}")
554
+ .rstrip("/")
555
+ .replace("?", ".")
556
+ )
557
+ + "$"
558
+ )
559
+ pattern = re.sub("[*]{2}", "=PLACEHOLDER=", pattern)
560
+ pattern = re.sub("[*]", "[^/]*", pattern)
561
+ pattern = re.compile(pattern.replace("=PLACEHOLDER=", ".*"))
562
+ out = {
563
+ p: allpaths[p]
564
+ for p in sorted(allpaths)
565
+ if pattern.match(p.replace("//", "/").rstrip("/"))
566
+ }
567
+ if detail:
568
+ return out
569
+ else:
570
+ return list(out)
571
+
572
+ def exists(self, path, **kwargs):
573
+ """Is there a file at the given path"""
574
+ try:
575
+ self.info(path, **kwargs)
576
+ return True
577
+ except: # noqa: E722
578
+ # any exception allowed bar FileNotFoundError?
579
+ return False
580
+
581
+ def lexists(self, path, **kwargs):
582
+ """If there is a file at the given path (including
583
+ broken links)"""
584
+ return self.exists(path)
585
+
586
+ def info(self, path, **kwargs):
587
+ """Give details of entry at path
588
+
589
+ Returns a single dictionary, with exactly the same information as ``ls``
590
+ would with ``detail=True``.
591
+
592
+ The default implementation should calls ls and could be overridden by a
593
+ shortcut. kwargs are passed on to ```ls()``.
594
+
595
+ Some file systems might not be able to measure the file's size, in
596
+ which case, the returned dict will include ``'size': None``.
597
+
598
+ Returns
599
+ -------
600
+ dict with keys: name (full path in the FS), size (in bytes), type (file,
601
+ directory, or something else) and other FS-specific keys.
602
+ """
603
+ path = self._strip_protocol(path)
604
+ out = self.ls(self._parent(path), detail=True, **kwargs)
605
+ out = [o for o in out if o["name"].rstrip("/") == path]
606
+ if out:
607
+ return out[0]
608
+ out = self.ls(path, detail=True, **kwargs)
609
+ path = path.rstrip("/")
610
+ out1 = [o for o in out if o["name"].rstrip("/") == path]
611
+ if len(out1) == 1:
612
+ if "size" not in out1[0]:
613
+ out1[0]["size"] = None
614
+ return out1[0]
615
+ elif len(out1) > 1 or out:
616
+ return {"name": path, "size": 0, "type": "directory"}
617
+ else:
618
+ raise FileNotFoundError(path)
619
+
620
+ def checksum(self, path):
621
+ """Unique value for current version of file
622
+
623
+ If the checksum is the same from one moment to another, the contents
624
+ are guaranteed to be the same. If the checksum changes, the contents
625
+ *might* have changed.
626
+
627
+ This should normally be overridden; default will probably capture
628
+ creation/modification timestamp (which would be good) or maybe
629
+ access timestamp (which would be bad)
630
+ """
631
+ return int(tokenize(self.info(path)), 16)
632
+
633
+ def size(self, path):
634
+ """Size in bytes of file"""
635
+ return self.info(path).get("size", None)
636
+
637
+ def sizes(self, paths):
638
+ """Size in bytes of each file in a list of paths"""
639
+ return [self.size(p) for p in paths]
640
+
641
+ def isdir(self, path):
642
+ """Is this entry directory-like?"""
643
+ try:
644
+ return self.info(path)["type"] == "directory"
645
+ except IOError:
646
+ return False
647
+
648
+ def isfile(self, path):
649
+ """Is this entry file-like?"""
650
+ try:
651
+ return self.info(path)["type"] == "file"
652
+ except: # noqa: E722
653
+ return False
654
+
655
+ def cat_file(self, path, start=None, end=None, **kwargs):
656
+ """Get the content of a file
657
+
658
+ Parameters
659
+ ----------
660
+ path: URL of file on this filesystems
661
+ start, end: int
662
+ Bytes limits of the read. If negative, backwards from end,
663
+ like usual python slices. Either can be None for start or
664
+ end of file, respectively
665
+ kwargs: passed to ``open()``.
666
+ """
667
+ # explicitly set buffering off?
668
+ with self.open(path, "rb", **kwargs) as f:
669
+ if start is not None:
670
+ if start >= 0:
671
+ f.seek(start)
672
+ else:
673
+ f.seek(max(0, f.size + start))
674
+ if end is not None:
675
+ if end < 0:
676
+ end = f.size + end
677
+ return f.read(end - f.tell())
678
+ return f.read()
679
+
680
+ def pipe_file(self, path, value, **kwargs):
681
+ """Set the bytes of given file"""
682
+ with self.open(path, "wb") as f:
683
+ f.write(value)
684
+
685
+ def pipe(self, path, value=None, **kwargs):
686
+ """Put value into path
687
+
688
+ (counterpart to ``cat``)
689
+
690
+ Parameters
691
+ ----------
692
+ path: string or dict(str, bytes)
693
+ If a string, a single remote location to put ``value`` bytes; if a dict,
694
+ a mapping of {path: bytesvalue}.
695
+ value: bytes, optional
696
+ If using a single path, these are the bytes to put there. Ignored if
697
+ ``path`` is a dict
698
+ """
699
+ if isinstance(path, str):
700
+ self.pipe_file(self._strip_protocol(path), value, **kwargs)
701
+ elif isinstance(path, dict):
702
+ for k, v in path.items():
703
+ self.pipe_file(self._strip_protocol(k), v, **kwargs)
704
+ else:
705
+ raise ValueError("path must be str or dict")
706
+
707
+ def cat_ranges(self, paths, starts, ends, max_gap=None, **kwargs):
708
+ if max_gap is not None:
709
+ raise NotImplementedError
710
+ if not isinstance(paths, list):
711
+ raise TypeError
712
+ if not isinstance(starts, list):
713
+ starts = [starts] * len(paths)
714
+ if not isinstance(ends, list):
715
+ ends = [starts] * len(paths)
716
+ if len(starts) != len(paths) or len(ends) != len(paths):
717
+ raise ValueError
718
+ return [self.cat_file(p, s, e) for p, s, e in zip(paths, starts, ends)]
719
+
720
+ def cat(self, path, recursive=False, on_error="raise", **kwargs):
721
+ """Fetch (potentially multiple) paths' contents
722
+
723
+ Parameters
724
+ ----------
725
+ recursive: bool
726
+ If True, assume the path(s) are directories, and get all the
727
+ contained files
728
+ on_error : "raise", "omit", "return"
729
+ If raise, an underlying exception will be raised (converted to KeyError
730
+ if the type is in self.missing_exceptions); if omit, keys with exception
731
+ will simply not be included in the output; if "return", all keys are
732
+ included in the output, but the value will be bytes or an exception
733
+ instance.
734
+ kwargs: passed to cat_file
735
+
736
+ Returns
737
+ -------
738
+ dict of {path: contents} if there are multiple paths
739
+ or the path has been otherwise expanded
740
+ """
741
+ paths = self.expand_path(path, recursive=recursive)
742
+ if (
743
+ len(paths) > 1
744
+ or isinstance(path, list)
745
+ or paths[0] != self._strip_protocol(path)
746
+ ):
747
+ out = {}
748
+ for path in paths:
749
+ try:
750
+ out[path] = self.cat_file(path, **kwargs)
751
+ except Exception as e:
752
+ if on_error == "raise":
753
+ raise
754
+ if on_error == "return":
755
+ out[path] = e
756
+ return out
757
+ else:
758
+ return self.cat_file(paths[0], **kwargs)
759
+
760
+ def get_file(
761
+ self, rpath, lpath, callback=_DEFAULT_CALLBACK, outfile=None, **kwargs
762
+ ):
763
+ """Copy single remote file to local"""
764
+ if isfilelike(lpath):
765
+ outfile = lpath
766
+ else:
767
+ if self.isdir(rpath):
768
+ os.makedirs(lpath, exist_ok=True)
769
+ return None
770
+
771
+ if outfile is None:
772
+ outfile = open(lpath, "wb")
773
+
774
+ with self.open(rpath, "rb", **kwargs) as f1:
775
+ callback.set_size(getattr(f1, "size", None))
776
+ data = True
777
+ while data:
778
+ data = f1.read(self.blocksize)
779
+ segment_len = outfile.write(data)
780
+ callback.relative_update(segment_len)
781
+ if not isfilelike(lpath):
782
+ outfile.close()
783
+
784
+ def get(self, rpath, lpath, recursive=False, callback=_DEFAULT_CALLBACK, **kwargs):
785
+ """Copy file(s) to local.
786
+
787
+ Copies a specific file or tree of files (if recursive=True). If lpath
788
+ ends with a "/", it will be assumed to be a directory, and target files
789
+ will go within. Can submit a list of paths, which may be glob-patterns
790
+ and will be expanded.
791
+
792
+ Calls get_file for each source.
793
+ """
794
+ from .implementations.local import make_path_posix
795
+
796
+ if isinstance(lpath, str):
797
+ lpath = make_path_posix(lpath)
798
+ rpaths = self.expand_path(rpath, recursive=recursive)
799
+ lpaths = other_paths(rpaths, lpath)
800
+
801
+ callback.set_size(len(lpaths))
802
+ for lpath, rpath in callback.wrap(zip(lpaths, rpaths)):
803
+ callback.branch(rpath, lpath, kwargs)
804
+ self.get_file(rpath, lpath, **kwargs)
805
+
806
+ def put_file(self, lpath, rpath, callback=_DEFAULT_CALLBACK, **kwargs):
807
+ """Copy single file to remote"""
808
+ if os.path.isdir(lpath):
809
+ self.makedirs(rpath, exist_ok=True)
810
+ return None
811
+
812
+ with open(lpath, "rb") as f1:
813
+ size = f1.seek(0, 2)
814
+ callback.set_size(size)
815
+ f1.seek(0)
816
+
817
+ self.mkdirs(self._parent(os.fspath(rpath)), exist_ok=True)
818
+ with self.open(rpath, "wb", **kwargs) as f2:
819
+ while f1.tell() < size:
820
+ data = f1.read(self.blocksize)
821
+ segment_len = f2.write(data)
822
+ callback.relative_update(segment_len)
823
+
824
+ def put(self, lpath, rpath, recursive=False, callback=_DEFAULT_CALLBACK, **kwargs):
825
+ """Copy file(s) from local.
826
+
827
+ Copies a specific file or tree of files (if recursive=True). If rpath
828
+ ends with a "/", it will be assumed to be a directory, and target files
829
+ will go within.
830
+
831
+ Calls put_file for each source.
832
+ """
833
+ from .implementations.local import LocalFileSystem, make_path_posix
834
+
835
+ rpath = (
836
+ self._strip_protocol(rpath)
837
+ if isinstance(rpath, str)
838
+ else [self._strip_protocol(p) for p in rpath]
839
+ )
840
+ if isinstance(lpath, str):
841
+ lpath = make_path_posix(lpath)
842
+ fs = LocalFileSystem()
843
+ lpaths = fs.expand_path(lpath, recursive=recursive)
844
+ rpaths = other_paths(
845
+ lpaths, rpath, exists=isinstance(rpath, str) and self.isdir(rpath)
846
+ )
847
+
848
+ callback.set_size(len(rpaths))
849
+ for lpath, rpath in callback.wrap(zip(lpaths, rpaths)):
850
+ callback.branch(lpath, rpath, kwargs)
851
+ self.put_file(lpath, rpath, **kwargs)
852
+
853
+ def head(self, path, size=1024):
854
+ """Get the first ``size`` bytes from file"""
855
+ with self.open(path, "rb") as f:
856
+ return f.read(size)
857
+
858
+ def tail(self, path, size=1024):
859
+ """Get the last ``size`` bytes from file"""
860
+ with self.open(path, "rb") as f:
861
+ f.seek(max(-size, -f.size), 2)
862
+ return f.read()
863
+
864
+ def cp_file(self, path1, path2, **kwargs):
865
+ raise NotImplementedError
866
+
867
+ def copy(self, path1, path2, recursive=False, on_error=None, **kwargs):
868
+ """Copy within two locations in the filesystem
869
+
870
+ on_error : "raise", "ignore"
871
+ If raise, any not-found exceptions will be raised; if ignore any
872
+ not-found exceptions will cause the path to be skipped; defaults to
873
+ raise unless recursive is true, where the default is ignore
874
+ """
875
+ if on_error is None and recursive:
876
+ on_error = "ignore"
877
+ elif on_error is None:
878
+ on_error = "raise"
879
+
880
+ paths = self.expand_path(path1, recursive=recursive)
881
+ path2 = other_paths(paths, path2)
882
+ for p1, p2 in zip(paths, path2):
883
+ try:
884
+ self.cp_file(p1, p2, **kwargs)
885
+ except FileNotFoundError:
886
+ if on_error == "raise":
887
+ raise
888
+
889
+ def expand_path(self, path, recursive=False, maxdepth=None):
890
+ """Turn one or more globs or directories into a list of all matching paths
891
+ to files or directories."""
892
+ if isinstance(path, str):
893
+ out = self.expand_path([path], recursive, maxdepth)
894
+ else:
895
+ # reduce depth on each recursion level unless None or 0
896
+ maxdepth = maxdepth if not maxdepth else maxdepth - 1
897
+ out = set()
898
+ path = [self._strip_protocol(p) for p in path]
899
+ for p in path:
900
+ if has_magic(p):
901
+ bit = set(self.glob(p))
902
+ out |= bit
903
+ if recursive:
904
+ out |= set(
905
+ self.expand_path(
906
+ list(bit), recursive=recursive, maxdepth=maxdepth
907
+ )
908
+ )
909
+ continue
910
+ elif recursive:
911
+ rec = set(self.find(p, maxdepth=maxdepth, withdirs=True))
912
+ out |= rec
913
+ if p not in out and (recursive is False or self.exists(p)):
914
+ # should only check once, for the root
915
+ out.add(p)
916
+ if not out:
917
+ raise FileNotFoundError(path)
918
+ return list(sorted(out))
919
+
920
+ def mv(self, path1, path2, recursive=False, maxdepth=None, **kwargs):
921
+ """Move file(s) from one location to another"""
922
+ self.copy(path1, path2, recursive=recursive, maxdepth=maxdepth)
923
+ self.rm(path1, recursive=recursive)
924
+
925
+ def rm_file(self, path):
926
+ """Delete a file"""
927
+ self._rm(path)
928
+
929
+ def _rm(self, path):
930
+ """Delete one file"""
931
+ # this is the old name for the method, prefer rm_file
932
+ raise NotImplementedError
933
+
934
+ def rm(self, path, recursive=False, maxdepth=None):
935
+ """Delete files.
936
+
937
+ Parameters
938
+ ----------
939
+ path: str or list of str
940
+ File(s) to delete.
941
+ recursive: bool
942
+ If file(s) are directories, recursively delete contents and then
943
+ also remove the directory
944
+ maxdepth: int or None
945
+ Depth to pass to walk for finding files to delete, if recursive.
946
+ If None, there will be no limit and infinite recursion may be
947
+ possible.
948
+ """
949
+ path = self.expand_path(path, recursive=recursive, maxdepth=maxdepth)
950
+ for p in reversed(path):
951
+ self.rm_file(p)
952
+
953
+ @classmethod
954
+ def _parent(cls, path):
955
+ path = cls._strip_protocol(path.rstrip("/"))
956
+ if "/" in path:
957
+ parent = path.rsplit("/", 1)[0].lstrip(cls.root_marker)
958
+ return cls.root_marker + parent
959
+ else:
960
+ return cls.root_marker
961
+
962
+ def _open(
963
+ self,
964
+ path,
965
+ mode="rb",
966
+ block_size=None,
967
+ autocommit=True,
968
+ cache_options=None,
969
+ **kwargs,
970
+ ):
971
+ """Return raw bytes-mode file-like from the file-system"""
972
+ return AbstractBufferedFile(
973
+ self,
974
+ path,
975
+ mode,
976
+ block_size,
977
+ autocommit,
978
+ cache_options=cache_options,
979
+ **kwargs,
980
+ )
981
+
982
+ def open(
983
+ self,
984
+ path,
985
+ mode="rb",
986
+ block_size=None,
987
+ cache_options=None,
988
+ compression=None,
989
+ **kwargs,
990
+ ):
991
+ """
992
+ Return a file-like object from the filesystem
993
+
994
+ The resultant instance must function correctly in a context ``with``
995
+ block.
996
+
997
+ Parameters
998
+ ----------
999
+ path: str
1000
+ Target file
1001
+ mode: str like 'rb', 'w'
1002
+ See builtin ``open()``
1003
+ block_size: int
1004
+ Some indication of buffering - this is a value in bytes
1005
+ cache_options : dict, optional
1006
+ Extra arguments to pass through to the cache.
1007
+ compression: string or None
1008
+ If given, open file using compression codec. Can either be a compression
1009
+ name (a key in ``fsspec.compression.compr``) or "infer" to guess the
1010
+ compression from the filename suffix.
1011
+ encoding, errors, newline: passed on to TextIOWrapper for text mode
1012
+ """
1013
+ import io
1014
+
1015
+ path = self._strip_protocol(path)
1016
+ if "b" not in mode:
1017
+ mode = mode.replace("t", "") + "b"
1018
+
1019
+ text_kwargs = {
1020
+ k: kwargs.pop(k)
1021
+ for k in ["encoding", "errors", "newline"]
1022
+ if k in kwargs
1023
+ }
1024
+ return io.TextIOWrapper(
1025
+ self.open(
1026
+ path,
1027
+ mode,
1028
+ block_size=block_size,
1029
+ cache_options=cache_options,
1030
+ compression=compression,
1031
+ **kwargs,
1032
+ ),
1033
+ **text_kwargs,
1034
+ )
1035
+ else:
1036
+ ac = kwargs.pop("autocommit", not self._intrans)
1037
+ f = self._open(
1038
+ path,
1039
+ mode=mode,
1040
+ block_size=block_size,
1041
+ autocommit=ac,
1042
+ cache_options=cache_options,
1043
+ **kwargs,
1044
+ )
1045
+ if compression is not None:
1046
+ from fsspec.compression import compr
1047
+ from fsspec.core import get_compression
1048
+
1049
+ compression = get_compression(path, compression)
1050
+ compress = compr[compression]
1051
+ f = compress(f, mode=mode[0])
1052
+
1053
+ if not ac and "r" not in mode:
1054
+ self.transaction.files.append(f)
1055
+ return f
1056
+
1057
+ def touch(self, path, truncate=True, **kwargs):
1058
+ """Create empty file, or update timestamp
1059
+
1060
+ Parameters
1061
+ ----------
1062
+ path: str
1063
+ file location
1064
+ truncate: bool
1065
+ If True, always set file size to 0; if False, update timestamp and
1066
+ leave file unchanged, if backend allows this
1067
+ """
1068
+ if truncate or not self.exists(path):
1069
+ with self.open(path, "wb", **kwargs):
1070
+ pass
1071
+ else:
1072
+ raise NotImplementedError # update timestamp, if possible
1073
+
1074
+ def ukey(self, path):
1075
+ """Hash of file properties, to tell if it has changed"""
1076
+ return sha256(str(self.info(path)).encode()).hexdigest()
1077
+
1078
+ def read_block(self, fn, offset, length, delimiter=None):
1079
+ """Read a block of bytes from
1080
+
1081
+ Starting at ``offset`` of the file, read ``length`` bytes. If
1082
+ ``delimiter`` is set then we ensure that the read starts and stops at
1083
+ delimiter boundaries that follow the locations ``offset`` and ``offset
1084
+ + length``. If ``offset`` is zero then we start at zero. The
1085
+ bytestring returned WILL include the end delimiter string.
1086
+
1087
+ If offset+length is beyond the eof, reads to eof.
1088
+
1089
+ Parameters
1090
+ ----------
1091
+ fn: string
1092
+ Path to filename
1093
+ offset: int
1094
+ Byte offset to start read
1095
+ length: int
1096
+ Number of bytes to read
1097
+ delimiter: bytes (optional)
1098
+ Ensure reading starts and stops at delimiter bytestring
1099
+
1100
+ Examples
1101
+ --------
1102
+ >>> fs.read_block('data/file.csv', 0, 13) # doctest: +SKIP
1103
+ b'Alice, 100\\nBo'
1104
+ >>> fs.read_block('data/file.csv', 0, 13, delimiter=b'\\n') # doctest: +SKIP
1105
+ b'Alice, 100\\nBob, 200\\n'
1106
+
1107
+ Use ``length=None`` to read to the end of the file.
1108
+ >>> fs.read_block('data/file.csv', 0, None, delimiter=b'\\n') # doctest: +SKIP
1109
+ b'Alice, 100\\nBob, 200\\nCharlie, 300'
1110
+
1111
+ See Also
1112
+ --------
1113
+ utils.read_block
1114
+ """
1115
+ with self.open(fn, "rb") as f:
1116
+ size = f.size
1117
+ if length is None:
1118
+ length = size
1119
+ if size is not None and offset + length > size:
1120
+ length = size - offset
1121
+ return read_block(f, offset, length, delimiter)
1122
+
1123
+ def to_json(self):
1124
+ """
1125
+ JSON representation of this filesystem instance
1126
+
1127
+ Returns
1128
+ -------
1129
+ str: JSON structure with keys cls (the python location of this class),
1130
+ protocol (text name of this class's protocol, first one in case of
1131
+ multiple), args (positional args, usually empty), and all other
1132
+ kwargs as their own keys.
1133
+ """
1134
+ import json
1135
+
1136
+ cls = type(self)
1137
+ cls = ".".join((cls.__module__, cls.__name__))
1138
+ proto = (
1139
+ self.protocol[0]
1140
+ if isinstance(self.protocol, (tuple, list))
1141
+ else self.protocol
1142
+ )
1143
+ return json.dumps(
1144
+ dict(
1145
+ **{"cls": cls, "protocol": proto, "args": self.storage_args},
1146
+ **self.storage_options,
1147
+ )
1148
+ )
1149
+
1150
+ @staticmethod
1151
+ def from_json(blob):
1152
+ """
1153
+ Recreate a filesystem instance from JSON representation
1154
+
1155
+ See ``.to_json()`` for the expected structure of the input
1156
+
1157
+ Parameters
1158
+ ----------
1159
+ blob: str
1160
+
1161
+ Returns
1162
+ -------
1163
+ file system instance, not necessarily of this particular class.
1164
+ """
1165
+ import json
1166
+
1167
+ from .registry import _import_class, get_filesystem_class
1168
+
1169
+ dic = json.loads(blob)
1170
+ protocol = dic.pop("protocol")
1171
+ try:
1172
+ cls = _import_class(dic.pop("cls"))
1173
+ except (ImportError, ValueError, RuntimeError, KeyError):
1174
+ cls = get_filesystem_class(protocol)
1175
+ return cls(*dic.pop("args", ()), **dic)
1176
+
1177
+ def _get_pyarrow_filesystem(self):
1178
+ """
1179
+ Make a version of the FS instance which will be acceptable to pyarrow
1180
+ """
1181
+ # all instances already also derive from pyarrow
1182
+ return self
1183
+
1184
+ def get_mapper(self, root="", check=False, create=False, missing_exceptions=None):
1185
+ """Create key/value store based on this file-system
1186
+
1187
+ Makes a MutableMapping interface to the FS at the given root path.
1188
+ See ``fsspec.mapping.FSMap`` for further details.
1189
+ """
1190
+ from .mapping import FSMap
1191
+
1192
+ return FSMap(
1193
+ root,
1194
+ self,
1195
+ check=check,
1196
+ create=create,
1197
+ missing_exceptions=missing_exceptions,
1198
+ )
1199
+
1200
+ @classmethod
1201
+ def clear_instance_cache(cls):
1202
+ """
1203
+ Clear the cache of filesystem instances.
1204
+
1205
+ Notes
1206
+ -----
1207
+ Unless overridden by setting the ``cachable`` class attribute to False,
1208
+ the filesystem class stores a reference to newly created instances. This
1209
+ prevents Python's normal rules around garbage collection from working,
1210
+ since the instances refcount will not drop to zero until
1211
+ ``clear_instance_cache`` is called.
1212
+ """
1213
+ cls._cache.clear()
1214
+
1215
+ def created(self, path):
1216
+ """Return the created timestamp of a file as a datetime.datetime"""
1217
+ raise NotImplementedError
1218
+
1219
+ def modified(self, path):
1220
+ """Return the modified timestamp of a file as a datetime.datetime"""
1221
+ raise NotImplementedError
1222
+
1223
+ # ------------------------------------------------------------------------
1224
+ # Aliases
1225
+
1226
+ def makedir(self, path, create_parents=True, **kwargs):
1227
+ """Alias of `AbstractFileSystem.mkdir`."""
1228
+ return self.mkdir(path, create_parents=create_parents, **kwargs)
1229
+
1230
+ def mkdirs(self, path, exist_ok=False):
1231
+ """Alias of `AbstractFileSystem.makedirs`."""
1232
+ return self.makedirs(path, exist_ok=exist_ok)
1233
+
1234
+ def listdir(self, path, detail=True, **kwargs):
1235
+ """Alias of `AbstractFileSystem.ls`."""
1236
+ return self.ls(path, detail=detail, **kwargs)
1237
+
1238
+ def cp(self, path1, path2, **kwargs):
1239
+ """Alias of `AbstractFileSystem.copy`."""
1240
+ return self.copy(path1, path2, **kwargs)
1241
+
1242
+ def move(self, path1, path2, **kwargs):
1243
+ """Alias of `AbstractFileSystem.mv`."""
1244
+ return self.mv(path1, path2, **kwargs)
1245
+
1246
+ def stat(self, path, **kwargs):
1247
+ """Alias of `AbstractFileSystem.info`."""
1248
+ return self.info(path, **kwargs)
1249
+
1250
+ def disk_usage(self, path, total=True, maxdepth=None, **kwargs):
1251
+ """Alias of `AbstractFileSystem.du`."""
1252
+ return self.du(path, total=total, maxdepth=maxdepth, **kwargs)
1253
+
1254
+ def rename(self, path1, path2, **kwargs):
1255
+ """Alias of `AbstractFileSystem.mv`."""
1256
+ return self.mv(path1, path2, **kwargs)
1257
+
1258
+ def delete(self, path, recursive=False, maxdepth=None):
1259
+ """Alias of `AbstractFileSystem.rm`."""
1260
+ return self.rm(path, recursive=recursive, maxdepth=maxdepth)
1261
+
1262
+ def upload(self, lpath, rpath, recursive=False, **kwargs):
1263
+ """Alias of `AbstractFileSystem.put`."""
1264
+ return self.put(lpath, rpath, recursive=recursive, **kwargs)
1265
+
1266
+ def download(self, rpath, lpath, recursive=False, **kwargs):
1267
+ """Alias of `AbstractFileSystem.get`."""
1268
+ return self.get(rpath, lpath, recursive=recursive, **kwargs)
1269
+
1270
+ def sign(self, path, expiration=100, **kwargs):
1271
+ """Create a signed URL representing the given path
1272
+
1273
+ Some implementations allow temporary URLs to be generated, as a
1274
+ way of delegating credentials.
1275
+
1276
+ Parameters
1277
+ ----------
1278
+ path : str
1279
+ The path on the filesystem
1280
+ expiration : int
1281
+ Number of seconds to enable the URL for (if supported)
1282
+
1283
+ Returns
1284
+ -------
1285
+ URL : str
1286
+ The signed URL
1287
+
1288
+ Raises
1289
+ ------
1290
+ NotImplementedError : if method is not implemented for a filesystem
1291
+ """
1292
+ raise NotImplementedError("Sign is not implemented for this filesystem")
1293
+
1294
+ def _isfilestore(self):
1295
+ # Originally inherited from pyarrow DaskFileSystem. Keeping this
1296
+ # here for backwards compatibility as long as pyarrow uses its
1297
+ # legacy fsspec-compatible filesystems and thus accepts fsspec
1298
+ # filesystems as well
1299
+ return False
1300
+
1301
+
1302
+ class AbstractBufferedFile(io.IOBase):
1303
+ """Convenient class to derive from to provide buffering
1304
+
1305
+ In the case that the backend does not provide a pythonic file-like object
1306
+ already, this class contains much of the logic to build one. The only
1307
+ methods that need to be overridden are ``_upload_chunk``,
1308
+ ``_initiate_upload`` and ``_fetch_range``.
1309
+ """
1310
+
1311
+ DEFAULT_BLOCK_SIZE = 5 * 2**20
1312
+ _details = None
1313
+
1314
+ def __init__(
1315
+ self,
1316
+ fs,
1317
+ path,
1318
+ mode="rb",
1319
+ block_size="default",
1320
+ autocommit=True,
1321
+ cache_type="readahead",
1322
+ cache_options=None,
1323
+ size=None,
1324
+ **kwargs,
1325
+ ):
1326
+ """
1327
+ Template for files with buffered reading and writing
1328
+
1329
+ Parameters
1330
+ ----------
1331
+ fs: instance of FileSystem
1332
+ path: str
1333
+ location in file-system
1334
+ mode: str
1335
+ Normal file modes. Currently only 'wb', 'ab' or 'rb'. Some file
1336
+ systems may be read-only, and some may not support append.
1337
+ block_size: int
1338
+ Buffer size for reading or writing, 'default' for class default
1339
+ autocommit: bool
1340
+ Whether to write to final destination; may only impact what
1341
+ happens when file is being closed.
1342
+ cache_type: {"readahead", "none", "mmap", "bytes"}, default "readahead"
1343
+ Caching policy in read mode. See the definitions in ``core``.
1344
+ cache_options : dict
1345
+ Additional options passed to the constructor for the cache specified
1346
+ by `cache_type`.
1347
+ size: int
1348
+ If given and in read mode, suppressed having to look up the file size
1349
+ kwargs:
1350
+ Gets stored as self.kwargs
1351
+ """
1352
+ from .core import caches
1353
+
1354
+ self.path = path
1355
+ self.fs = fs
1356
+ self.mode = mode
1357
+ self.blocksize = (
1358
+ self.DEFAULT_BLOCK_SIZE if block_size in ["default", None] else block_size
1359
+ )
1360
+ self.loc = 0
1361
+ self.autocommit = autocommit
1362
+ self.end = None
1363
+ self.start = None
1364
+ self.closed = False
1365
+
1366
+ if cache_options is None:
1367
+ cache_options = {}
1368
+
1369
+ if "trim" in kwargs:
1370
+ warnings.warn(
1371
+ "Passing 'trim' to control the cache behavior has been deprecated. "
1372
+ "Specify it within the 'cache_options' argument instead.",
1373
+ FutureWarning,
1374
+ )
1375
+ cache_options["trim"] = kwargs.pop("trim")
1376
+
1377
+ self.kwargs = kwargs
1378
+
1379
+ if mode not in {"ab", "rb", "wb"}:
1380
+ raise NotImplementedError("File mode not supported")
1381
+ if mode == "rb":
1382
+ if size is not None:
1383
+ self.size = size
1384
+ else:
1385
+ self.size = self.details["size"]
1386
+ self.cache = caches[cache_type](
1387
+ self.blocksize, self._fetch_range, self.size, **cache_options
1388
+ )
1389
+ else:
1390
+ self.buffer = io.BytesIO()
1391
+ self.offset = None
1392
+ self.forced = False
1393
+ self.location = None
1394
+
1395
+ @property
1396
+ def details(self):
1397
+ if self._details is None:
1398
+ self._details = self.fs.info(self.path)
1399
+ return self._details
1400
+
1401
+ @details.setter
1402
+ def details(self, value):
1403
+ self._details = value
1404
+ self.size = value["size"]
1405
+
1406
+ @property
1407
+ def full_name(self):
1408
+ return _unstrip_protocol(self.path, self.fs)
1409
+
1410
+ @property
1411
+ def closed(self):
1412
+ # get around this attr being read-only in IOBase
1413
+ # use getattr here, since this can be called during del
1414
+ return getattr(self, "_closed", True)
1415
+
1416
+ @closed.setter
1417
+ def closed(self, c):
1418
+ self._closed = c
1419
+
1420
+ def __hash__(self):
1421
+ if "w" in self.mode:
1422
+ return id(self)
1423
+ else:
1424
+ return int(tokenize(self.details), 16)
1425
+
1426
+ def __eq__(self, other):
1427
+ """Files are equal if they have the same checksum, only in read mode"""
1428
+ return self.mode == "rb" and other.mode == "rb" and hash(self) == hash(other)
1429
+
1430
+ def commit(self):
1431
+ """Move from temp to final destination"""
1432
+
1433
+ def discard(self):
1434
+ """Throw away temporary file"""
1435
+
1436
+ def info(self):
1437
+ """File information about this path"""
1438
+ if "r" in self.mode:
1439
+ return self.details
1440
+ else:
1441
+ raise ValueError("Info not available while writing")
1442
+
1443
+ def tell(self):
1444
+ """Current file location"""
1445
+ return self.loc
1446
+
1447
+ def seek(self, loc, whence=0):
1448
+ """Set current file location
1449
+
1450
+ Parameters
1451
+ ----------
1452
+ loc: int
1453
+ byte location
1454
+ whence: {0, 1, 2}
1455
+ from start of file, current location or end of file, resp.
1456
+ """
1457
+ loc = int(loc)
1458
+ if not self.mode == "rb":
1459
+ raise OSError(ESPIPE, "Seek only available in read mode")
1460
+ if whence == 0:
1461
+ nloc = loc
1462
+ elif whence == 1:
1463
+ nloc = self.loc + loc
1464
+ elif whence == 2:
1465
+ nloc = self.size + loc
1466
+ else:
1467
+ raise ValueError("invalid whence (%s, should be 0, 1 or 2)" % whence)
1468
+ if nloc < 0:
1469
+ raise ValueError("Seek before start of file")
1470
+ self.loc = nloc
1471
+ return self.loc
1472
+
1473
+ def write(self, data):
1474
+ """
1475
+ Write data to buffer.
1476
+
1477
+ Buffer only sent on flush() or if buffer is greater than
1478
+ or equal to blocksize.
1479
+
1480
+ Parameters
1481
+ ----------
1482
+ data: bytes
1483
+ Set of bytes to be written.
1484
+ """
1485
+ if self.mode not in {"wb", "ab"}:
1486
+ raise ValueError("File not in write mode")
1487
+ if self.closed:
1488
+ raise ValueError("I/O operation on closed file.")
1489
+ if self.forced:
1490
+ raise ValueError("This file has been force-flushed, can only close")
1491
+ out = self.buffer.write(data)
1492
+ self.loc += out
1493
+ if self.buffer.tell() >= self.blocksize:
1494
+ self.flush()
1495
+ return out
1496
+
1497
+ def flush(self, force=False):
1498
+ """
1499
+ Write buffered data to backend store.
1500
+
1501
+ Writes the current buffer, if it is larger than the block-size, or if
1502
+ the file is being closed.
1503
+
1504
+ Parameters
1505
+ ----------
1506
+ force: bool
1507
+ When closing, write the last block even if it is smaller than
1508
+ blocks are allowed to be. Disallows further writing to this file.
1509
+ """
1510
+
1511
+ if self.closed:
1512
+ raise ValueError("Flush on closed file")
1513
+ if force and self.forced:
1514
+ raise ValueError("Force flush cannot be called more than once")
1515
+ if force:
1516
+ self.forced = True
1517
+
1518
+ if self.mode not in {"wb", "ab"}:
1519
+ # no-op to flush on read-mode
1520
+ return
1521
+
1522
+ if not force and self.buffer.tell() < self.blocksize:
1523
+ # Defer write on small block
1524
+ return
1525
+
1526
+ if self.offset is None:
1527
+ # Initialize a multipart upload
1528
+ self.offset = 0
1529
+ try:
1530
+ self._initiate_upload()
1531
+ except: # noqa: E722
1532
+ self.closed = True
1533
+ raise
1534
+
1535
+ if self._upload_chunk(final=force) is not False:
1536
+ self.offset += self.buffer.seek(0, 2)
1537
+ self.buffer = io.BytesIO()
1538
+
1539
+ def _upload_chunk(self, final=False):
1540
+ """Write one part of a multi-block file upload
1541
+
1542
+ Parameters
1543
+ ==========
1544
+ final: bool
1545
+ This is the last block, so should complete file, if
1546
+ self.autocommit is True.
1547
+ """
1548
+ # may not yet have been initialized, may need to call _initialize_upload
1549
+
1550
+ def _initiate_upload(self):
1551
+ """Create remote file/upload"""
1552
+ pass
1553
+
1554
+ def _fetch_range(self, start, end):
1555
+ """Get the specified set of bytes from remote"""
1556
+ raise NotImplementedError
1557
+
1558
+ def read(self, length=-1):
1559
+ """
1560
+ Return data from cache, or fetch pieces as necessary
1561
+
1562
+ Parameters
1563
+ ----------
1564
+ length: int (-1)
1565
+ Number of bytes to read; if <0, all remaining bytes.
1566
+ """
1567
+ length = -1 if length is None else int(length)
1568
+ if self.mode != "rb":
1569
+ raise ValueError("File not in read mode")
1570
+ if length < 0:
1571
+ length = self.size - self.loc
1572
+ if self.closed:
1573
+ raise ValueError("I/O operation on closed file.")
1574
+ logger.debug("%s read: %i - %i" % (self, self.loc, self.loc + length))
1575
+ if length == 0:
1576
+ # don't even bother calling fetch
1577
+ return b""
1578
+ out = self.cache._fetch(self.loc, self.loc + length)
1579
+ self.loc += len(out)
1580
+ return out
1581
+
1582
+ def readinto(self, b):
1583
+ """mirrors builtin file's readinto method
1584
+
1585
+ https://docs.python.org/3/library/io.html#io.RawIOBase.readinto
1586
+ """
1587
+ out = memoryview(b).cast("B")
1588
+ data = self.read(out.nbytes)
1589
+ out[: len(data)] = data
1590
+ return len(data)
1591
+
1592
+ def readuntil(self, char=b"\n", blocks=None):
1593
+ """Return data between current position and first occurrence of char
1594
+
1595
+ char is included in the output, except if the end of the tile is
1596
+ encountered first.
1597
+
1598
+ Parameters
1599
+ ----------
1600
+ char: bytes
1601
+ Thing to find
1602
+ blocks: None or int
1603
+ How much to read in each go. Defaults to file blocksize - which may
1604
+ mean a new read on every call.
1605
+ """
1606
+ out = []
1607
+ while True:
1608
+ start = self.tell()
1609
+ part = self.read(blocks or self.blocksize)
1610
+ if len(part) == 0:
1611
+ break
1612
+ found = part.find(char)
1613
+ if found > -1:
1614
+ out.append(part[: found + len(char)])
1615
+ self.seek(start + found + len(char))
1616
+ break
1617
+ out.append(part)
1618
+ return b"".join(out)
1619
+
1620
+ def readline(self):
1621
+ """Read until first occurrence of newline character
1622
+
1623
+ Note that, because of character encoding, this is not necessarily a
1624
+ true line ending.
1625
+ """
1626
+ return self.readuntil(b"\n")
1627
+
1628
+ def __next__(self):
1629
+ out = self.readline()
1630
+ if out:
1631
+ return out
1632
+ raise StopIteration
1633
+
1634
+ def __iter__(self):
1635
+ return self
1636
+
1637
+ def readlines(self):
1638
+ """Return all data, split by the newline character"""
1639
+ data = self.read()
1640
+ lines = data.split(b"\n")
1641
+ out = [l + b"\n" for l in lines[:-1]]
1642
+ if data.endswith(b"\n"):
1643
+ return out
1644
+ else:
1645
+ return out + [lines[-1]]
1646
+ # return list(self) ???
1647
+
1648
+ def readinto1(self, b):
1649
+ return self.readinto(b)
1650
+
1651
+ def close(self):
1652
+ """Close file
1653
+
1654
+ Finalizes writes, discards cache
1655
+ """
1656
+ if getattr(self, "_unclosable", False):
1657
+ return
1658
+ if self.closed:
1659
+ return
1660
+ if self.mode == "rb":
1661
+ self.cache = None
1662
+ else:
1663
+ if not self.forced:
1664
+ self.flush(force=True)
1665
+
1666
+ if self.fs is not None:
1667
+ self.fs.invalidate_cache(self.path)
1668
+ self.fs.invalidate_cache(self.fs._parent(self.path))
1669
+
1670
+ self.closed = True
1671
+
1672
+ def readable(self):
1673
+ """Whether opened for reading"""
1674
+ return self.mode == "rb" and not self.closed
1675
+
1676
+ def seekable(self):
1677
+ """Whether is seekable (only in read mode)"""
1678
+ return self.readable()
1679
+
1680
+ def writable(self):
1681
+ """Whether opened for writing"""
1682
+ return self.mode in {"wb", "ab"} and not self.closed
1683
+
1684
+ def __del__(self):
1685
+ if not self.closed:
1686
+ self.close()
1687
+
1688
+ def __str__(self):
1689
+ return "<File-like object %s, %s>" % (type(self.fs).__name__, self.path)
1690
+
1691
+ __repr__ = __str__
1692
+
1693
+ def __enter__(self):
1694
+ return self
1695
+
1696
+ def __exit__(self, *args):
1697
+ self.close()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/transaction.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Transaction(object):
2
+ """Filesystem transaction write context
3
+
4
+ Gathers files for deferred commit or discard, so that several write
5
+ operations can be finalized semi-atomically. This works by having this
6
+ instance as the ``.transaction`` attribute of the given filesystem
7
+ """
8
+
9
+ def __init__(self, fs):
10
+ """
11
+ Parameters
12
+ ----------
13
+ fs: FileSystem instance
14
+ """
15
+ self.fs = fs
16
+ self.files = []
17
+
18
+ def __enter__(self):
19
+ self.start()
20
+
21
+ def __exit__(self, exc_type, exc_val, exc_tb):
22
+ """End transaction and commit, if exit is not due to exception"""
23
+ # only commit if there was no exception
24
+ self.complete(commit=exc_type is None)
25
+ self.fs._intrans = False
26
+ self.fs._transaction = None
27
+
28
+ def start(self):
29
+ """Start a transaction on this FileSystem"""
30
+ self.files = [] # clean up after previous failed completions
31
+ self.fs._intrans = True
32
+
33
+ def complete(self, commit=True):
34
+ """Finish transaction: commit or discard all deferred files"""
35
+ for f in self.files:
36
+ if commit:
37
+ f.commit()
38
+ else:
39
+ f.discard()
40
+ self.files = []
41
+ self.fs._intrans = False
42
+
43
+
44
+ class FileActor(object):
45
+ def __init__(self):
46
+ self.files = []
47
+
48
+ def commit(self):
49
+ for f in self.files:
50
+ f.commit()
51
+ self.files.clear()
52
+
53
+ def discard(self):
54
+ for f in self.files:
55
+ f.discard()
56
+ self.files.clear()
57
+
58
+ def append(self, f):
59
+ self.files.append(f)
60
+
61
+
62
+ class DaskTransaction(Transaction):
63
+ def __init__(self, fs):
64
+ """
65
+ Parameters
66
+ ----------
67
+ fs: FileSystem instance
68
+ """
69
+ import distributed
70
+
71
+ super().__init__(fs)
72
+ client = distributed.default_client()
73
+ self.files = client.submit(FileActor, actor=True).result()
74
+
75
+ def complete(self, commit=True):
76
+ """Finish transaction: commit or discard all deferred files"""
77
+ if commit:
78
+ self.files.commit().result()
79
+ else:
80
+ self.files.discard().result()
81
+ self.fs._intrans = False
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/fsspec/utils.py ADDED
@@ -0,0 +1,621 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import math
3
+ import os
4
+ import pathlib
5
+ import re
6
+ import sys
7
+ from contextlib import contextmanager
8
+ from functools import partial
9
+ from hashlib import md5
10
+ from types import TracebackType
11
+ from typing import IO, AnyStr, Callable, Iterable, Iterator, List, Optional, Type
12
+ from urllib.parse import urlsplit
13
+
14
+ DEFAULT_BLOCK_SIZE = 5 * 2**20
15
+
16
+
17
+ def infer_storage_options(urlpath, inherit_storage_options=None):
18
+ """Infer storage options from URL path and merge it with existing storage
19
+ options.
20
+
21
+ Parameters
22
+ ----------
23
+ urlpath: str or unicode
24
+ Either local absolute file path or URL (hdfs://namenode:8020/file.csv)
25
+ inherit_storage_options: dict (optional)
26
+ Its contents will get merged with the inferred information from the
27
+ given path
28
+
29
+ Returns
30
+ -------
31
+ Storage options dict.
32
+
33
+ Examples
34
+ --------
35
+ >>> infer_storage_options('/mnt/datasets/test.csv') # doctest: +SKIP
36
+ {"protocol": "file", "path", "/mnt/datasets/test.csv"}
37
+ >>> infer_storage_options(
38
+ ... 'hdfs://username:pwd@node:123/mnt/datasets/test.csv?q=1',
39
+ ... inherit_storage_options={'extra': 'value'},
40
+ ... ) # doctest: +SKIP
41
+ {"protocol": "hdfs", "username": "username", "password": "pwd",
42
+ "host": "node", "port": 123, "path": "/mnt/datasets/test.csv",
43
+ "url_query": "q=1", "extra": "value"}
44
+ """
45
+ # Handle Windows paths including disk name in this special case
46
+ if (
47
+ re.match(r"^[a-zA-Z]:[\\/]", urlpath)
48
+ or re.match(r"^[a-zA-Z0-9]+://", urlpath) is None
49
+ ):
50
+ return {"protocol": "file", "path": urlpath}
51
+
52
+ parsed_path = urlsplit(urlpath)
53
+ protocol = parsed_path.scheme or "file"
54
+ if parsed_path.fragment:
55
+ path = "#".join([parsed_path.path, parsed_path.fragment])
56
+ else:
57
+ path = parsed_path.path
58
+ if protocol == "file":
59
+ # Special case parsing file protocol URL on Windows according to:
60
+ # https://msdn.microsoft.com/en-us/library/jj710207.aspx
61
+ windows_path = re.match(r"^/([a-zA-Z])[:|]([\\/].*)$", path)
62
+ if windows_path:
63
+ path = "%s:%s" % windows_path.groups()
64
+
65
+ if protocol in ["http", "https"]:
66
+ # for HTTP, we don't want to parse, as requests will anyway
67
+ return {"protocol": protocol, "path": urlpath}
68
+
69
+ options = {"protocol": protocol, "path": path}
70
+
71
+ if parsed_path.netloc:
72
+ # Parse `hostname` from netloc manually because `parsed_path.hostname`
73
+ # lowercases the hostname which is not always desirable (e.g. in S3):
74
+ # https://github.com/dask/dask/issues/1417
75
+ options["host"] = parsed_path.netloc.rsplit("@", 1)[-1].rsplit(":", 1)[0]
76
+
77
+ if protocol in ("s3", "s3a", "gcs", "gs"):
78
+ options["path"] = options["host"] + options["path"]
79
+ else:
80
+ options["host"] = options["host"]
81
+ if parsed_path.port:
82
+ options["port"] = parsed_path.port
83
+ if parsed_path.username:
84
+ options["username"] = parsed_path.username
85
+ if parsed_path.password:
86
+ options["password"] = parsed_path.password
87
+
88
+ if parsed_path.query:
89
+ options["url_query"] = parsed_path.query
90
+ if parsed_path.fragment:
91
+ options["url_fragment"] = parsed_path.fragment
92
+
93
+ if inherit_storage_options:
94
+ update_storage_options(options, inherit_storage_options)
95
+
96
+ return options
97
+
98
+
99
+ def update_storage_options(options, inherited=None):
100
+ if not inherited:
101
+ inherited = {}
102
+ collisions = set(options) & set(inherited)
103
+ if collisions:
104
+ for collision in collisions:
105
+ if options.get(collision) != inherited.get(collision):
106
+ raise KeyError(
107
+ "Collision between inferred and specified storage "
108
+ "option:\n%s" % collision
109
+ )
110
+ options.update(inherited)
111
+
112
+
113
+ # Compression extensions registered via fsspec.compression.register_compression
114
+ compressions = {}
115
+
116
+
117
+ def infer_compression(filename):
118
+ """Infer compression, if available, from filename.
119
+
120
+ Infer a named compression type, if registered and available, from filename
121
+ extension. This includes builtin (gz, bz2, zip) compressions, as well as
122
+ optional compressions. See fsspec.compression.register_compression.
123
+ """
124
+ extension = os.path.splitext(filename)[-1].strip(".").lower()
125
+ if extension in compressions:
126
+ return compressions[extension]
127
+
128
+
129
+ def build_name_function(max_int):
130
+ """Returns a function that receives a single integer
131
+ and returns it as a string padded by enough zero characters
132
+ to align with maximum possible integer
133
+
134
+ >>> name_f = build_name_function(57)
135
+
136
+ >>> name_f(7)
137
+ '07'
138
+ >>> name_f(31)
139
+ '31'
140
+ >>> build_name_function(1000)(42)
141
+ '0042'
142
+ >>> build_name_function(999)(42)
143
+ '042'
144
+ >>> build_name_function(0)(0)
145
+ '0'
146
+ """
147
+ # handle corner cases max_int is 0 or exact power of 10
148
+ max_int += 1e-8
149
+
150
+ pad_length = int(math.ceil(math.log10(max_int)))
151
+
152
+ def name_function(i):
153
+ return str(i).zfill(pad_length)
154
+
155
+ return name_function
156
+
157
+
158
+ def seek_delimiter(file, delimiter, blocksize):
159
+ r"""Seek current file to file start, file end, or byte after delimiter seq.
160
+
161
+ Seeks file to next chunk delimiter, where chunks are defined on file start,
162
+ a delimiting sequence, and file end. Use file.tell() to see location afterwards.
163
+ Note that file start is a valid split, so must be at offset > 0 to seek for
164
+ delimiter.
165
+
166
+ Parameters
167
+ ----------
168
+ file: a file
169
+ delimiter: bytes
170
+ a delimiter like ``b'\n'`` or message sentinel, matching file .read() type
171
+ blocksize: int
172
+ Number of bytes to read from the file at once.
173
+
174
+
175
+ Returns
176
+ -------
177
+ Returns True if a delimiter was found, False if at file start or end.
178
+
179
+ """
180
+
181
+ if file.tell() == 0:
182
+ # beginning-of-file, return without seek
183
+ return False
184
+
185
+ # Interface is for binary IO, with delimiter as bytes, but initialize last
186
+ # with result of file.read to preserve compatibility with text IO.
187
+ last = None
188
+ while True:
189
+ current = file.read(blocksize)
190
+ if not current:
191
+ # end-of-file without delimiter
192
+ return False
193
+ full = last + current if last else current
194
+ try:
195
+ if delimiter in full:
196
+ i = full.index(delimiter)
197
+ file.seek(file.tell() - (len(full) - i) + len(delimiter))
198
+ return True
199
+ elif len(current) < blocksize:
200
+ # end-of-file without delimiter
201
+ return False
202
+ except (OSError, ValueError):
203
+ pass
204
+ last = full[-len(delimiter) :]
205
+
206
+
207
+ def read_block(f, offset, length, delimiter=None, split_before=False):
208
+ """Read a block of bytes from a file
209
+
210
+ Parameters
211
+ ----------
212
+ f: File
213
+ Open file
214
+ offset: int
215
+ Byte offset to start read
216
+ length: int
217
+ Number of bytes to read, read through end of file if None
218
+ delimiter: bytes (optional)
219
+ Ensure reading starts and stops at delimiter bytestring
220
+ split_before: bool (optional)
221
+ Start/stop read *before* delimiter bytestring.
222
+
223
+
224
+ If using the ``delimiter=`` keyword argument we ensure that the read
225
+ starts and stops at delimiter boundaries that follow the locations
226
+ ``offset`` and ``offset + length``. If ``offset`` is zero then we
227
+ start at zero, regardless of delimiter. The bytestring returned WILL
228
+ include the terminating delimiter string.
229
+
230
+ Examples
231
+ --------
232
+
233
+ >>> from io import BytesIO # doctest: +SKIP
234
+ >>> f = BytesIO(b'Alice, 100\\nBob, 200\\nCharlie, 300') # doctest: +SKIP
235
+ >>> read_block(f, 0, 13) # doctest: +SKIP
236
+ b'Alice, 100\\nBo'
237
+
238
+ >>> read_block(f, 0, 13, delimiter=b'\\n') # doctest: +SKIP
239
+ b'Alice, 100\\nBob, 200\\n'
240
+
241
+ >>> read_block(f, 10, 10, delimiter=b'\\n') # doctest: +SKIP
242
+ b'Bob, 200\\nCharlie, 300'
243
+ """
244
+ if delimiter:
245
+ f.seek(offset)
246
+ found_start_delim = seek_delimiter(f, delimiter, 2**16)
247
+ if length is None:
248
+ return f.read()
249
+ start = f.tell()
250
+ length -= start - offset
251
+
252
+ f.seek(start + length)
253
+ found_end_delim = seek_delimiter(f, delimiter, 2**16)
254
+ end = f.tell()
255
+
256
+ # Adjust split location to before delimiter iff seek found the
257
+ # delimiter sequence, not start or end of file.
258
+ if found_start_delim and split_before:
259
+ start -= len(delimiter)
260
+
261
+ if found_end_delim and split_before:
262
+ end -= len(delimiter)
263
+
264
+ offset = start
265
+ length = end - start
266
+
267
+ f.seek(offset)
268
+ b = f.read(length)
269
+ return b
270
+
271
+
272
+ def tokenize(*args, **kwargs):
273
+ """Deterministic token
274
+
275
+ (modified from dask.base)
276
+
277
+ >>> tokenize([1, 2, '3'])
278
+ '9d71491b50023b06fc76928e6eddb952'
279
+
280
+ >>> tokenize('Hello') == tokenize('Hello')
281
+ True
282
+ """
283
+ if kwargs:
284
+ args += (kwargs,)
285
+ try:
286
+ return md5(str(args).encode()).hexdigest()
287
+ except ValueError:
288
+ # FIPS systems: https://github.com/fsspec/filesystem_spec/issues/380
289
+ return md5(str(args).encode(), usedforsecurity=False).hexdigest()
290
+
291
+
292
+ def stringify_path(filepath):
293
+ """Attempt to convert a path-like object to a string.
294
+
295
+ Parameters
296
+ ----------
297
+ filepath: object to be converted
298
+
299
+ Returns
300
+ -------
301
+ filepath_str: maybe a string version of the object
302
+
303
+ Notes
304
+ -----
305
+ Objects supporting the fspath protocol are coerced according to its
306
+ __fspath__ method.
307
+
308
+ For backwards compatibility with older Python version, pathlib.Path
309
+ objects are specially coerced.
310
+
311
+ Any other object is passed through unchanged, which includes bytes,
312
+ strings, buffers, or anything else that's not even path-like.
313
+ """
314
+ if isinstance(filepath, str):
315
+ return filepath
316
+ elif hasattr(filepath, "__fspath__"):
317
+ return filepath.__fspath__()
318
+ elif isinstance(filepath, pathlib.Path):
319
+ return str(filepath)
320
+ elif hasattr(filepath, "path"):
321
+ return filepath.path
322
+ else:
323
+ return filepath
324
+
325
+
326
+ def make_instance(cls, args, kwargs):
327
+ inst = cls(*args, **kwargs)
328
+ inst._determine_worker()
329
+ return inst
330
+
331
+
332
+ def common_prefix(paths):
333
+ """For a list of paths, find the shortest prefix common to all"""
334
+ parts = [p.split("/") for p in paths]
335
+ lmax = min(len(p) for p in parts)
336
+ end = 0
337
+ for i in range(lmax):
338
+ end = all(p[i] == parts[0][i] for p in parts)
339
+ if not end:
340
+ break
341
+ i += end
342
+ return "/".join(parts[0][:i])
343
+
344
+
345
+ def other_paths(paths, path2, is_dir=None, exists=False):
346
+ """In bulk file operations, construct a new file tree from a list of files
347
+
348
+ Parameters
349
+ ----------
350
+ paths: list of str
351
+ The input file tree
352
+ path2: str or list of str
353
+ Root to construct the new list in. If this is already a list of str, we just
354
+ assert it has the right number of elements.
355
+ is_dir: bool (optional)
356
+ For the special case where the input in one element, whether to regard the value
357
+ as the target path, or as a directory to put a file path within. If None, a
358
+ directory is inferred if the path ends in '/'
359
+ exists: bool (optional)
360
+ For a str destination, it is already exists (and is a dir), files should
361
+ end up inside.
362
+
363
+ Returns
364
+ -------
365
+ list of str
366
+ """
367
+ if isinstance(path2, str):
368
+ is_dir = is_dir or path2.endswith("/")
369
+ path2 = path2.rstrip("/")
370
+ if len(paths) > 1:
371
+ cp = common_prefix(paths)
372
+ if exists:
373
+ cp = cp.rsplit("/", 1)[0]
374
+ path2 = [p.replace(cp, path2, 1) for p in paths]
375
+ else:
376
+ if is_dir:
377
+ path2 = [path2.rstrip("/") + "/" + paths[0].rsplit("/")[-1]]
378
+ else:
379
+ path2 = [path2]
380
+ else:
381
+ assert len(paths) == len(path2)
382
+ return path2
383
+
384
+
385
+ def is_exception(obj):
386
+ return isinstance(obj, BaseException)
387
+
388
+
389
+ def isfilelike(f):
390
+ for attr in ["read", "close", "tell"]:
391
+ if not hasattr(f, attr):
392
+ return False
393
+ return True
394
+
395
+
396
+ def get_protocol(url):
397
+ parts = re.split(r"(\:\:|\://)", url, 1)
398
+ if len(parts) > 1:
399
+ return parts[0]
400
+ return "file"
401
+
402
+
403
+ def can_be_local(path):
404
+ """Can the given URL be used with open_local?"""
405
+ from fsspec import get_filesystem_class
406
+
407
+ try:
408
+ return getattr(get_filesystem_class(get_protocol(path)), "local_file", False)
409
+ except (ValueError, ImportError):
410
+ # not in registry or import failed
411
+ return False
412
+
413
+
414
+ def get_package_version_without_import(name):
415
+ """For given package name, try to find the version without importing it
416
+
417
+ Import and package.__version__ is still the backup here, so an import
418
+ *might* happen.
419
+
420
+ Returns either the version string, or None if the package
421
+ or the version was not readily found.
422
+ """
423
+ if name in sys.modules:
424
+ mod = sys.modules[name]
425
+ if hasattr(mod, "__version__"):
426
+ return mod.__version__
427
+ if sys.version_info >= (3, 8):
428
+ try:
429
+ import importlib.metadata
430
+
431
+ return importlib.metadata.distribution(name).version
432
+ except: # noqa: E722
433
+ pass
434
+ else:
435
+ try:
436
+ import importlib_metadata
437
+
438
+ return importlib_metadata.distribution(name).version
439
+ except: # noqa: E722
440
+ pass
441
+ try:
442
+ import importlib
443
+
444
+ mod = importlib.import_module(name)
445
+ return mod.__version__
446
+ except (ImportError, AttributeError):
447
+ return None
448
+
449
+
450
+ def setup_logging(logger=None, logger_name=None, level="DEBUG", clear=True):
451
+ if logger is None and logger_name is None:
452
+ raise ValueError("Provide either logger object or logger name")
453
+ logger = logger or logging.getLogger(logger_name)
454
+ handle = logging.StreamHandler()
455
+ formatter = logging.Formatter(
456
+ "%(asctime)s - %(name)s - %(levelname)s - %(funcName)s -- %(message)s"
457
+ )
458
+ handle.setFormatter(formatter)
459
+ if clear:
460
+ logger.handlers.clear()
461
+ logger.addHandler(handle)
462
+ logger.setLevel(level)
463
+ return logger
464
+
465
+
466
+ def _unstrip_protocol(name, fs):
467
+ return fs.unstrip_protocol(name)
468
+
469
+
470
+ def mirror_from(origin_name, methods):
471
+ """Mirror attributes and methods from the given
472
+ origin_name attribute of the instance to the
473
+ decorated class"""
474
+
475
+ def origin_getter(method, self):
476
+ origin = getattr(self, origin_name)
477
+ return getattr(origin, method)
478
+
479
+ def wrapper(cls):
480
+ for method in methods:
481
+ wrapped_method = partial(origin_getter, method)
482
+ setattr(cls, method, property(wrapped_method))
483
+ return cls
484
+
485
+ return wrapper
486
+
487
+
488
+ @contextmanager
489
+ def nullcontext(obj):
490
+ yield obj
491
+
492
+
493
+ def merge_offset_ranges(paths, starts, ends, max_gap=0, max_block=None, sort=True):
494
+ """Merge adjacent byte-offset ranges when the inter-range
495
+ gap is <= `max_gap`, and when the merged byte range does not
496
+ exceed `max_block` (if specified). By default, this function
497
+ will re-order the input paths and byte ranges to ensure sorted
498
+ order. If the user can guarantee that the inputs are already
499
+ sorted, passing `sort=False` will skip the re-ordering.
500
+ """
501
+
502
+ # Check input
503
+ if not isinstance(paths, list):
504
+ raise TypeError
505
+ if not isinstance(starts, list):
506
+ starts = [starts] * len(paths)
507
+ if not isinstance(ends, list):
508
+ ends = [starts] * len(paths)
509
+ if len(starts) != len(paths) or len(ends) != len(paths):
510
+ raise ValueError
511
+
512
+ # Early Return
513
+ if len(starts) <= 1:
514
+ return paths, starts, ends
515
+
516
+ # Sort by paths and then ranges if `sort=True`
517
+ if sort:
518
+ paths, starts, ends = [list(v) for v in zip(*sorted(zip(paths, starts, ends)))]
519
+
520
+ if paths:
521
+ # Loop through the coupled `paths`, `starts`, and
522
+ # `ends`, and merge adjacent blocks when appropriate
523
+ new_paths = paths[:1]
524
+ new_starts = starts[:1]
525
+ new_ends = ends[:1]
526
+ for i in range(1, len(paths)):
527
+ if (
528
+ paths[i] != paths[i - 1]
529
+ or ((starts[i] - new_ends[-1]) > max_gap)
530
+ or ((max_block is not None and (ends[i] - new_starts[-1]) > max_block))
531
+ ):
532
+ # Cannot merge with previous block.
533
+ # Add new `paths`, `starts`, and `ends` elements
534
+ new_paths.append(paths[i])
535
+ new_starts.append(starts[i])
536
+ new_ends.append(ends[i])
537
+ else:
538
+ # Merge with previous block by updating the
539
+ # last element of `ends`
540
+ new_ends[-1] = ends[i]
541
+ return new_paths, new_starts, new_ends
542
+
543
+ # `paths` is empty. Just return input lists
544
+ return paths, starts, ends
545
+
546
+
547
+ class IOWrapper(IO):
548
+ """Wrapper for a file-like object that can be used in situations where we might
549
+ want to, e.g., monkey-patch the close method but can't.
550
+ (cf https://github.com/fsspec/filesystem_spec/issues/725)
551
+ """
552
+
553
+ def __init__(self, fp: IO, closer: Callable[[], None]):
554
+ self.fp = fp
555
+ self.closer = closer
556
+
557
+ def close(self) -> None:
558
+ self.fp.close()
559
+
560
+ def fileno(self) -> int:
561
+ return self.fp.fileno()
562
+
563
+ def flush(self) -> None:
564
+ self.fp.flush()
565
+
566
+ def isatty(self) -> bool:
567
+ return self.fp.isatty()
568
+
569
+ def read(self, n: int = ...) -> AnyStr:
570
+ return self.fp.read(n)
571
+
572
+ def readable(self) -> bool:
573
+ return self.fp.readable()
574
+
575
+ def readline(self, limit: int = ...) -> AnyStr:
576
+ return self.fp.readline(limit)
577
+
578
+ def readlines(self, hint: int = ...) -> List[AnyStr]:
579
+ return self.fp.readlines(hint)
580
+
581
+ def seek(self, offset: int, whence: int = ...) -> int:
582
+ return self.fp.seek(offset, whence)
583
+
584
+ def seekable(self) -> bool:
585
+ return self.fp.seekable()
586
+
587
+ def tell(self) -> int:
588
+ return self.fp.tell()
589
+
590
+ def truncate(self, size: Optional[int] = ...) -> int:
591
+ return self.fp.truncate(size)
592
+
593
+ def writable(self) -> bool:
594
+ return self.fp.writable()
595
+
596
+ def write(self, s: AnyStr) -> int:
597
+ return self.fp.write(s)
598
+
599
+ def writelines(self, lines: Iterable[AnyStr]) -> None:
600
+ self.fp.writelines(lines)
601
+
602
+ def __next__(self) -> AnyStr:
603
+ return next(self.fp)
604
+
605
+ def __iter__(self) -> Iterator[AnyStr]:
606
+ return iter(self.fp)
607
+
608
+ def __enter__(self) -> IO[AnyStr]:
609
+ return self.fp.__enter__()
610
+
611
+ def __exit__(
612
+ self,
613
+ t: Optional[Type[BaseException]],
614
+ value: Optional[BaseException],
615
+ traceback: Optional[TracebackType],
616
+ ) -> Optional[bool]:
617
+ return self.fp.__exit__(t, value, traceback)
618
+
619
+ # forward anything else too
620
+ def __getattr__(self, name):
621
+ return getattr(self.fp, name)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2013-2019 Python Charmers Pty Ltd, Australia
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/METADATA ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.1
2
+ Name: future
3
+ Version: 0.18.2
4
+ Summary: Clean single-source support for Python 3 and 2
5
+ Home-page: https://python-future.org
6
+ Author: Ed Schofield
7
+ Author-email: ed@pythoncharmers.com
8
+ License: MIT
9
+ Keywords: future past python3 migration futurize backport six 2to3 modernize pasteurize 3to2
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 2
12
+ Classifier: Programming Language :: Python :: 2.6
13
+ Classifier: Programming Language :: Python :: 2.7
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.3
16
+ Classifier: Programming Language :: Python :: 3.4
17
+ Classifier: Programming Language :: Python :: 3.5
18
+ Classifier: Programming Language :: Python :: 3.6
19
+ Classifier: Programming Language :: Python :: 3.7
20
+ Classifier: License :: OSI Approved
21
+ Classifier: License :: OSI Approved :: MIT License
22
+ Classifier: Development Status :: 4 - Beta
23
+ Classifier: Intended Audience :: Developers
24
+ Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
25
+ License-File: LICENSE.txt
26
+
27
+
28
+ future: Easy, safe support for Python 2/3 compatibility
29
+ =======================================================
30
+
31
+ ``future`` is the missing compatibility layer between Python 2 and Python
32
+ 3. It allows you to use a single, clean Python 3.x-compatible codebase to
33
+ support both Python 2 and Python 3 with minimal overhead.
34
+
35
+ It is designed to be used as follows::
36
+
37
+ from __future__ import (absolute_import, division,
38
+ print_function, unicode_literals)
39
+ from builtins import (
40
+ bytes, dict, int, list, object, range, str,
41
+ ascii, chr, hex, input, next, oct, open,
42
+ pow, round, super,
43
+ filter, map, zip)
44
+
45
+ followed by predominantly standard, idiomatic Python 3 code that then runs
46
+ similarly on Python 2.6/2.7 and Python 3.3+.
47
+
48
+ The imports have no effect on Python 3. On Python 2, they shadow the
49
+ corresponding builtins, which normally have different semantics on Python 3
50
+ versus 2, to provide their Python 3 semantics.
51
+
52
+
53
+ Standard library reorganization
54
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
55
+
56
+ ``future`` supports the standard library reorganization (PEP 3108) through the
57
+ following Py3 interfaces:
58
+
59
+ >>> # Top-level packages with Py3 names provided on Py2:
60
+ >>> import html.parser
61
+ >>> import queue
62
+ >>> import tkinter.dialog
63
+ >>> import xmlrpc.client
64
+ >>> # etc.
65
+
66
+ >>> # Aliases provided for extensions to existing Py2 module names:
67
+ >>> from future.standard_library import install_aliases
68
+ >>> install_aliases()
69
+
70
+ >>> from collections import Counter, OrderedDict # backported to Py2.6
71
+ >>> from collections import UserDict, UserList, UserString
72
+ >>> import urllib.request
73
+ >>> from itertools import filterfalse, zip_longest
74
+ >>> from subprocess import getoutput, getstatusoutput
75
+
76
+
77
+ Automatic conversion
78
+ --------------------
79
+
80
+ An included script called `futurize
81
+ <http://python-future.org/automatic_conversion.html>`_ aids in converting
82
+ code (from either Python 2 or Python 3) to code compatible with both
83
+ platforms. It is similar to ``python-modernize`` but goes further in
84
+ providing Python 3 compatibility through the use of the backported types
85
+ and builtin functions in ``future``.
86
+
87
+
88
+ Documentation
89
+ -------------
90
+
91
+ See: http://python-future.org
92
+
93
+
94
+ Credits
95
+ -------
96
+
97
+ :Author: Ed Schofield, Jordan M. Adler, et al
98
+ :Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
99
+ Ltd, Singapore. http://pythoncharmers.com
100
+ :Others: See docs/credits.rst or http://python-future.org/credits.html
101
+
102
+
103
+ Licensing
104
+ ---------
105
+ Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
106
+ The software is distributed under an MIT licence. See LICENSE.txt.
107
+
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/RECORD ADDED
@@ -0,0 +1,416 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ../../../bin/futurize,sha256=xKA_N8JYnXCyzVBfTUkz9pU3eXH5d8c16svN3USrvM4,233
2
+ ../../../bin/pasteurize,sha256=GLIzOFPYAgHM9EHgTergrPuJuATuCzdvhzyepiNMEz8,235
3
+ future-0.18.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
4
+ future-0.18.2.dist-info/LICENSE.txt,sha256=kW5WE5LUhHG5wjQ39W4mUvMgyzsRnOqhYu30EBb3Rrk,1083
5
+ future-0.18.2.dist-info/METADATA,sha256=TpD2d2mu5U0tn4-xlVPIWgr_ND-bC8Bp0OSqjDXk2rw,3709
6
+ future-0.18.2.dist-info/RECORD,,
7
+ future-0.18.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ future-0.18.2.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
9
+ future-0.18.2.dist-info/entry_points.txt,sha256=U9LtP60KSNXoj58mzV5TbotBF371gTWzrKrzJIH80Kw,88
10
+ future-0.18.2.dist-info/top_level.txt,sha256=DT0C3az2gb-uJaj-fs0h4WwHYlJVDp0EvLdud1y5Zyw,38
11
+ future/__init__.py,sha256=TsDq1XoGk6Jfach_rEhwAi07zR5OKYZ6hhUlG5Bj6Ag,2991
12
+ future/__pycache__/__init__.cpython-38.pyc,,
13
+ future/backports/__init__.py,sha256=5QXvQ_jc5Xx6p4dSaHnZXPZazBEunKDKhbUjxZ0XD1I,530
14
+ future/backports/__pycache__/__init__.cpython-38.pyc,,
15
+ future/backports/__pycache__/_markupbase.cpython-38.pyc,,
16
+ future/backports/__pycache__/datetime.cpython-38.pyc,,
17
+ future/backports/__pycache__/misc.cpython-38.pyc,,
18
+ future/backports/__pycache__/socket.cpython-38.pyc,,
19
+ future/backports/__pycache__/socketserver.cpython-38.pyc,,
20
+ future/backports/__pycache__/total_ordering.cpython-38.pyc,,
21
+ future/backports/_markupbase.py,sha256=MDPTCykLq4J7Aea3PvYotATEE0CG4R_SjlxfJaLXTJM,16215
22
+ future/backports/datetime.py,sha256=I214Vu0cRY8mi8J5aIcsAyQJnWmOKXeLV-QTWSn7VQU,75552
23
+ future/backports/email/__init__.py,sha256=eH3AJr3FkuBy_D6yS1V2K76Q2CQ93y2zmAMWmn8FbHI,2269
24
+ future/backports/email/__pycache__/__init__.cpython-38.pyc,,
25
+ future/backports/email/__pycache__/_encoded_words.cpython-38.pyc,,
26
+ future/backports/email/__pycache__/_header_value_parser.cpython-38.pyc,,
27
+ future/backports/email/__pycache__/_parseaddr.cpython-38.pyc,,
28
+ future/backports/email/__pycache__/_policybase.cpython-38.pyc,,
29
+ future/backports/email/__pycache__/base64mime.cpython-38.pyc,,
30
+ future/backports/email/__pycache__/charset.cpython-38.pyc,,
31
+ future/backports/email/__pycache__/encoders.cpython-38.pyc,,
32
+ future/backports/email/__pycache__/errors.cpython-38.pyc,,
33
+ future/backports/email/__pycache__/feedparser.cpython-38.pyc,,
34
+ future/backports/email/__pycache__/generator.cpython-38.pyc,,
35
+ future/backports/email/__pycache__/header.cpython-38.pyc,,
36
+ future/backports/email/__pycache__/headerregistry.cpython-38.pyc,,
37
+ future/backports/email/__pycache__/iterators.cpython-38.pyc,,
38
+ future/backports/email/__pycache__/message.cpython-38.pyc,,
39
+ future/backports/email/__pycache__/parser.cpython-38.pyc,,
40
+ future/backports/email/__pycache__/policy.cpython-38.pyc,,
41
+ future/backports/email/__pycache__/quoprimime.cpython-38.pyc,,
42
+ future/backports/email/__pycache__/utils.cpython-38.pyc,,
43
+ future/backports/email/_encoded_words.py,sha256=m1vTRfxAQdg4VyWO7PF-1ih1mmq97V-BPyHHkuEwSME,8443
44
+ future/backports/email/_header_value_parser.py,sha256=cj_1ce1voLn8H98r9cKqiSLgfFSxCv3_UL3sSvjqgjk,104692
45
+ future/backports/email/_parseaddr.py,sha256=KewEnos0YDM-SYX503z7E1MmVbG5VRaKjxjcl0Ipjbs,17389
46
+ future/backports/email/_policybase.py,sha256=2lJD9xouiz4uHvWGQ6j1nwlwWVQGwwzpy5JZoeQqhUc,14647
47
+ future/backports/email/base64mime.py,sha256=sey6iJA9pHIOdFgoV1p7QAwYVjt8CEkDhITt304-nyI,3729
48
+ future/backports/email/charset.py,sha256=CfE4iV2zAq6MQC0CHXHLnwTNW71zmhNITbzOcfxE4vY,17439
49
+ future/backports/email/encoders.py,sha256=Nn4Pcx1rOdRgoSIzB6T5RWHl5zxClbf32wgE6D0tUt8,2800
50
+ future/backports/email/errors.py,sha256=tRX8PP5g7mk2bAxL1jTCYrbfhD2gPZFNrh4_GJRM8OQ,3680
51
+ future/backports/email/feedparser.py,sha256=bvmhb4cdY-ipextPK2K2sDgMsNvTspmuQfYyCxc4zSc,22736
52
+ future/backports/email/generator.py,sha256=lpaLhZHneguvZ2QgRu7Figkjb7zmY28AGhj9iZTdI7s,19520
53
+ future/backports/email/header.py,sha256=uBHbNKO-yx5I9KBflernJpyy3fX4gImCB1QE7ICApLs,24448
54
+ future/backports/email/headerregistry.py,sha256=ZPbvLKXD0NMLSU4jXlVHfGyGcLMrFm-GQVURu_XHj88,20637
55
+ future/backports/email/iterators.py,sha256=kMRYFGy3SVVpo7HG7JJr2ZAlOoaX6CVPzKYwDSvLfV0,2348
56
+ future/backports/email/message.py,sha256=I6WW5cZDza7uwLOGJSvsDhGZC9K_Q570Lk2gt_vDUXM,35237
57
+ future/backports/email/mime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
+ future/backports/email/mime/__pycache__/__init__.cpython-38.pyc,,
59
+ future/backports/email/mime/__pycache__/application.cpython-38.pyc,,
60
+ future/backports/email/mime/__pycache__/audio.cpython-38.pyc,,
61
+ future/backports/email/mime/__pycache__/base.cpython-38.pyc,,
62
+ future/backports/email/mime/__pycache__/image.cpython-38.pyc,,
63
+ future/backports/email/mime/__pycache__/message.cpython-38.pyc,,
64
+ future/backports/email/mime/__pycache__/multipart.cpython-38.pyc,,
65
+ future/backports/email/mime/__pycache__/nonmultipart.cpython-38.pyc,,
66
+ future/backports/email/mime/__pycache__/text.cpython-38.pyc,,
67
+ future/backports/email/mime/application.py,sha256=m-5a4mSxu2E32XAImnp9x9eMVX5Vme2iNgn2dMMNyss,1401
68
+ future/backports/email/mime/audio.py,sha256=2ognalFRadcsUYQYMUZbjv5i1xJbFhQN643doMuI7M4,2815
69
+ future/backports/email/mime/base.py,sha256=wV3ClQyMsOqmkXSXbk_wd_zPoPTvBx8kAIzq3rdM4lE,875
70
+ future/backports/email/mime/image.py,sha256=DpQk1sB-IMmO43AF4uadsXyf_y5TdEzJLfyhqR48bIw,1907
71
+ future/backports/email/mime/message.py,sha256=pFsMhXW07aRjsLq1peO847PApWFAl28-Z2Z7BP1Dn74,1429
72
+ future/backports/email/mime/multipart.py,sha256=j4Lf_sJmuwTbfgdQ6R35_t1_ha2DynJBJDvpjwbNObE,1699
73
+ future/backports/email/mime/nonmultipart.py,sha256=Ciba1Z8d2yLDDpxgDJuk3Bb-TqcpE9HCd8KfbW5vgl4,832
74
+ future/backports/email/mime/text.py,sha256=zV98BjoR4S_nX8c47x43LnsnifeGhIfNGwSAh575bs0,1552
75
+ future/backports/email/parser.py,sha256=-115SC3DHZ6lLijWFTxuOnE-GiM2BOYaUSz-QpmvYSo,5312
76
+ future/backports/email/policy.py,sha256=gpcbhVRXuCohkK6MUqopTs1lv4E4-ZVUO6OVncoGEJE,8823
77
+ future/backports/email/quoprimime.py,sha256=w93W5XgdFpyGaDqDBJrnXF_v_npH5r20WuAxmrAzyQg,10923
78
+ future/backports/email/utils.py,sha256=vpfN0E8UjNbNw-2NFBQGCo4TNgrghMsqzpEYW5C_fBs,14270
79
+ future/backports/html/__init__.py,sha256=FKwqFtWMCoGNkhU97OPnR1fZSh6etAKfN1FU1KvXcV8,924
80
+ future/backports/html/__pycache__/__init__.cpython-38.pyc,,
81
+ future/backports/html/__pycache__/entities.cpython-38.pyc,,
82
+ future/backports/html/__pycache__/parser.cpython-38.pyc,,
83
+ future/backports/html/entities.py,sha256=kzoRnQyGk_3DgoucHLhL5QL1pglK9nvmxhPIGZFDTnc,75428
84
+ future/backports/html/parser.py,sha256=G2tUObvbHSotNt06JLY-BP1swaZNfDYFd_ENWDjPmRg,19770
85
+ future/backports/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
+ future/backports/http/__pycache__/__init__.cpython-38.pyc,,
87
+ future/backports/http/__pycache__/client.cpython-38.pyc,,
88
+ future/backports/http/__pycache__/cookiejar.cpython-38.pyc,,
89
+ future/backports/http/__pycache__/cookies.cpython-38.pyc,,
90
+ future/backports/http/__pycache__/server.cpython-38.pyc,,
91
+ future/backports/http/client.py,sha256=76EbhEZOtvdHFcU-jrjivoff13oQ9IMbdkZEdf5kQzQ,47602
92
+ future/backports/http/cookiejar.py,sha256=_Vy4BPT-h0ZT0R_utGQAFXzuOAdmU9KedGFffyX9wN4,76559
93
+ future/backports/http/cookies.py,sha256=DsyDUGDEbCXAA9Jq6suswSc76uSZqUu39adDDNj8XGw,21581
94
+ future/backports/http/server.py,sha256=1CaMxgzHf9lYhmTJyE7topgjRIlIn9cnjgw8YEvwJV4,45523
95
+ future/backports/misc.py,sha256=AkbED6BdHKnYCmIAontT4zHKTqdPPfJfn35HIs6LDrg,32682
96
+ future/backports/socket.py,sha256=DH1V6IjKPpJ0tln8bYvxvQ7qnvZG-UoQtMA5yVleHiU,15663
97
+ future/backports/socketserver.py,sha256=Twvyk5FqVnOeiNcbVsyMDPTF1mNlkKfyofG7tKxTdD8,24286
98
+ future/backports/test/__init__.py,sha256=9dXxIZnkI095YfHC-XIaVF6d31GjeY1Ag8TEzcFgepM,264
99
+ future/backports/test/__pycache__/__init__.cpython-38.pyc,,
100
+ future/backports/test/__pycache__/pystone.cpython-38.pyc,,
101
+ future/backports/test/__pycache__/ssl_servers.cpython-38.pyc,,
102
+ future/backports/test/__pycache__/support.cpython-38.pyc,,
103
+ future/backports/test/badcert.pem,sha256=JioQeRZkHH8hGsWJjAF3U1zQvcWqhyzG6IOEJpTY9SE,1928
104
+ future/backports/test/badkey.pem,sha256=gaBK9px_gG7DmrLKxfD6f6i-toAmARBTVfs-YGFRQF0,2162
105
+ future/backports/test/dh512.pem,sha256=dUTsjtLbK-femrorUrTGF8qvLjhTiT_n4Uo5V6u__Gs,402
106
+ future/backports/test/https_svn_python_org_root.pem,sha256=wOB3Onnc62Iu9kEFd8GcHhd_suucYjpJNA3jyfHeJWA,2569
107
+ future/backports/test/keycert.passwd.pem,sha256=ZBfnVLpbBtAOf_2gCdiQ-yrBHmRsNzSf8VC3UpQZIjg,1830
108
+ future/backports/test/keycert.pem,sha256=xPXi5idPcQVbrhgxBqF2TNGm6sSZ2aLVVEt6DWzplL8,1783
109
+ future/backports/test/keycert2.pem,sha256=DB46FEAYv8BWwQJ-5RzC696FxPN7CON-Qsi-R4poJgc,1795
110
+ future/backports/test/nokia.pem,sha256=s00x0uPDSaa5DHJ_CwzlVhg3OVdJ47f4zgqQdd0SAfQ,1923
111
+ future/backports/test/nullbytecert.pem,sha256=NFRYWhmP_qT3jGfVjR6-iaC-EQdhIFjiXtTLN5ZPKnE,5435
112
+ future/backports/test/nullcert.pem,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
113
+ future/backports/test/pystone.py,sha256=fvyoJ_tVovTNaxbJmdJMwr9F6SngY-U4ibULnd_wUqA,7427
114
+ future/backports/test/sha256.pem,sha256=3wB-GQqEc7jq-PYwYAQaPbtTvvr7stk_DVmZxFgehfA,8344
115
+ future/backports/test/ssl_cert.pem,sha256=M607jJNeIeHG9BlTf_jaQkPJI4nOxSJPn-zmEAaW43M,867
116
+ future/backports/test/ssl_key.passwd.pem,sha256=I_WH4sBw9Vs9Z-BvmuXY0aw8tx8avv6rm5UL4S_pP00,963
117
+ future/backports/test/ssl_key.pem,sha256=VKGU-R3UYaZpVTXl7chWl4vEYEDeob69SfvRTQ8aq_4,916
118
+ future/backports/test/ssl_servers.py,sha256=-pd7HMZljuZfFRAbCAiAP_2G04orITJFj-S9ddr6o84,7209
119
+ future/backports/test/support.py,sha256=zJrb-pz-Wu2dZwnNodg1v3w96zVq7ORuN-hOGOHbdA8,70881
120
+ future/backports/total_ordering.py,sha256=O3M57_IisQ-zW5hW20uxkfk4fTGsr0EF2tAKx3BksQo,1929
121
+ future/backports/urllib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
+ future/backports/urllib/__pycache__/__init__.cpython-38.pyc,,
123
+ future/backports/urllib/__pycache__/error.cpython-38.pyc,,
124
+ future/backports/urllib/__pycache__/parse.cpython-38.pyc,,
125
+ future/backports/urllib/__pycache__/request.cpython-38.pyc,,
126
+ future/backports/urllib/__pycache__/response.cpython-38.pyc,,
127
+ future/backports/urllib/__pycache__/robotparser.cpython-38.pyc,,
128
+ future/backports/urllib/error.py,sha256=ktikuK9ag4lS4f8Z0k5p1F11qF40N2AiOtjbXiF97ew,2715
129
+ future/backports/urllib/parse.py,sha256=67avrYqV1UK7i_22goRUrvJ8SffzjRdTja9wzq_ynXY,35792
130
+ future/backports/urllib/request.py,sha256=aR9ZMzfhV1C2Qk3wFsGvkwxqtdPTdsJVGRt5DUCwgJ8,96276
131
+ future/backports/urllib/response.py,sha256=ooQyswwbb-9N6IVi1Kwjss1aR-Kvm8ZNezoyVEonp8c,3180
132
+ future/backports/urllib/robotparser.py,sha256=pnAGTbKhdbCq_9yMZp7m8hj5q_NJpyQX6oQIZuYcnkw,6865
133
+ future/backports/xmlrpc/__init__.py,sha256=h61ciVTdVvu8oEUXv4dHf_Tc5XUXDH3RKB1-8fQhSsg,38
134
+ future/backports/xmlrpc/__pycache__/__init__.cpython-38.pyc,,
135
+ future/backports/xmlrpc/__pycache__/client.cpython-38.pyc,,
136
+ future/backports/xmlrpc/__pycache__/server.cpython-38.pyc,,
137
+ future/backports/xmlrpc/client.py,sha256=6a6Pvx_RVC9gIHDkFOVdREeGaZckOOiWd7T6GyzU3qU,48133
138
+ future/backports/xmlrpc/server.py,sha256=W_RW5hgYbNV2LGbnvngzm7akacRdK-XFY-Cy2HL-qsY,37285
139
+ future/builtins/__init__.py,sha256=jSdOucWfCsfkfTR8Jd4-Ls-YQpJ0AnzUomBxgwuoxNs,1687
140
+ future/builtins/__pycache__/__init__.cpython-38.pyc,,
141
+ future/builtins/__pycache__/disabled.cpython-38.pyc,,
142
+ future/builtins/__pycache__/iterators.cpython-38.pyc,,
143
+ future/builtins/__pycache__/misc.cpython-38.pyc,,
144
+ future/builtins/__pycache__/new_min_max.cpython-38.pyc,,
145
+ future/builtins/__pycache__/newnext.cpython-38.pyc,,
146
+ future/builtins/__pycache__/newround.cpython-38.pyc,,
147
+ future/builtins/__pycache__/newsuper.cpython-38.pyc,,
148
+ future/builtins/disabled.py,sha256=Ysq74bsmwntpq7dzkwTAD7IHKrkXy66vJlPshVwgVBI,2109
149
+ future/builtins/iterators.py,sha256=l1Zawm2x82oqOuGGtCZRE76Ej98sMlHQwu9fZLK5RrA,1396
150
+ future/builtins/misc.py,sha256=hctlKKWUyN0Eoodxg4ySQHEqARTukOLR4L5K5c6PW9k,4550
151
+ future/builtins/new_min_max.py,sha256=7qQ4iiG4GDgRzjPzzzmg9pdby35Mtt6xNOOsyqHnIGY,1757
152
+ future/builtins/newnext.py,sha256=oxXB8baXqJv29YG40aCS9UXk9zObyoOjya8BJ7NdBJM,2009
153
+ future/builtins/newround.py,sha256=l2EXPAFU3fAsZigJxUH6x66B7jhNaB076-L5FR617R8,3181
154
+ future/builtins/newsuper.py,sha256=LmiUQ_f6NXDIz6v6sDPkoTWl-2Zccy7PpZfQKYtscac,4146
155
+ future/moves/__init__.py,sha256=MsAW69Xp_fqUo4xODufcKM6AZf-ozHaz44WPZdsDFJA,220
156
+ future/moves/__pycache__/__init__.cpython-38.pyc,,
157
+ future/moves/__pycache__/_dummy_thread.cpython-38.pyc,,
158
+ future/moves/__pycache__/_markupbase.cpython-38.pyc,,
159
+ future/moves/__pycache__/_thread.cpython-38.pyc,,
160
+ future/moves/__pycache__/builtins.cpython-38.pyc,,
161
+ future/moves/__pycache__/collections.cpython-38.pyc,,
162
+ future/moves/__pycache__/configparser.cpython-38.pyc,,
163
+ future/moves/__pycache__/copyreg.cpython-38.pyc,,
164
+ future/moves/__pycache__/itertools.cpython-38.pyc,,
165
+ future/moves/__pycache__/pickle.cpython-38.pyc,,
166
+ future/moves/__pycache__/queue.cpython-38.pyc,,
167
+ future/moves/__pycache__/reprlib.cpython-38.pyc,,
168
+ future/moves/__pycache__/socketserver.cpython-38.pyc,,
169
+ future/moves/__pycache__/subprocess.cpython-38.pyc,,
170
+ future/moves/__pycache__/sys.cpython-38.pyc,,
171
+ future/moves/__pycache__/winreg.cpython-38.pyc,,
172
+ future/moves/_dummy_thread.py,sha256=c8ZRUd8ffvyvGKGGgve5NKc8VdtAWquu8-4FnO2EdvA,175
173
+ future/moves/_markupbase.py,sha256=W9wh_Gu3jDAMIhVBV1ZnCkJwYLHRk_v_su_HLALBkZQ,171
174
+ future/moves/_thread.py,sha256=rwY7L4BZMFPlrp_i6T2Un4_iKYwnrXJ-yV6FJZN8YDo,163
175
+ future/moves/builtins.py,sha256=4sjjKiylecJeL9da_RaBZjdymX2jtMs84oA9lCqb4Ug,281
176
+ future/moves/collections.py,sha256=OKQ-TfUgms_2bnZRn4hrclLDoiN2i-HSWcjs3BC2iY8,417
177
+ future/moves/configparser.py,sha256=TNy226uCbljjU-DjAVo7j7Effbj5zxXvDh0SdXehbzk,146
178
+ future/moves/copyreg.py,sha256=Y3UjLXIMSOxZggXtvZucE9yv4tkKZtVan45z8eix4sU,438
179
+ future/moves/dbm/__init__.py,sha256=_VkvQHC2UcIgZFPRroiX_P0Fs7HNqS_69flR0-oq2B8,488
180
+ future/moves/dbm/__pycache__/__init__.cpython-38.pyc,,
181
+ future/moves/dbm/__pycache__/dumb.cpython-38.pyc,,
182
+ future/moves/dbm/__pycache__/gnu.cpython-38.pyc,,
183
+ future/moves/dbm/__pycache__/ndbm.cpython-38.pyc,,
184
+ future/moves/dbm/dumb.py,sha256=HKdjjtO3EyP9EKi1Hgxh_eUU6yCQ0fBX9NN3n-zb8JE,166
185
+ future/moves/dbm/gnu.py,sha256=XoCSEpZ2QaOgo2h1m80GW7NUgj_b93BKtbcuwgtnaKo,162
186
+ future/moves/dbm/ndbm.py,sha256=OFnreyo_1YHDBl5YUm9gCzKlN1MHgWbfSQAZVls2jaM,162
187
+ future/moves/html/__init__.py,sha256=BSUFSHxXf2kGvHozlnrB1nn6bPE6p4PpN3DwA_Z5geo,1016
188
+ future/moves/html/__pycache__/__init__.cpython-38.pyc,,
189
+ future/moves/html/__pycache__/entities.cpython-38.pyc,,
190
+ future/moves/html/__pycache__/parser.cpython-38.pyc,,
191
+ future/moves/html/entities.py,sha256=lVvchdjK_RzRj759eg4RMvGWHfgBbj0tKGOoZ8dbRyY,177
192
+ future/moves/html/parser.py,sha256=V2XpHLKLCxQum3N9xlO3IUccAD7BIykZMqdEcWET3vY,167
193
+ future/moves/http/__init__.py,sha256=Mx1v_Tcks4udHCtDM8q2xnYUiQ01gD7EpPyeQwsP3-Q,71
194
+ future/moves/http/__pycache__/__init__.cpython-38.pyc,,
195
+ future/moves/http/__pycache__/client.cpython-38.pyc,,
196
+ future/moves/http/__pycache__/cookiejar.cpython-38.pyc,,
197
+ future/moves/http/__pycache__/cookies.cpython-38.pyc,,
198
+ future/moves/http/__pycache__/server.cpython-38.pyc,,
199
+ future/moves/http/client.py,sha256=hqEBq7GDXZidd1AscKnSyjSoMcuj8rERqGTmD7VheDQ,165
200
+ future/moves/http/cookiejar.py,sha256=Frr9ZZCg-145ymy0VGpiPJhvBEpJtVqRBYPaKhgT1Z4,173
201
+ future/moves/http/cookies.py,sha256=PPrHa1_oDbu3D_BhJGc6PvMgY1KoxyYq1jqeJwEcMvE,233
202
+ future/moves/http/server.py,sha256=8YQlSCShjAsB5rr5foVvZgp3IzwYFvTmGZCHhBSDtaI,606
203
+ future/moves/itertools.py,sha256=PVxFHRlBQl9ElS0cuGFPcUtj53eHX7Z1DmggzGfgQ6c,158
204
+ future/moves/pickle.py,sha256=r8j9skzfE8ZCeHyh_OB-WucOkRTIHN7zpRM7l7V3qS4,229
205
+ future/moves/queue.py,sha256=uxvLCChF-zxWWgrY1a_wxt8rp2jILdwO4PrnkBW6VTE,160
206
+ future/moves/reprlib.py,sha256=Nt5sUgMQ3jeVIukqSHOvB0UIsl6Y5t-mmT_13mpZmiY,161
207
+ future/moves/socketserver.py,sha256=v8ZLurDxHOgsubYm1iefjlpnnJQcx2VuRUGt9FCJB9k,174
208
+ future/moves/subprocess.py,sha256=oqRSMfFZkxM4MXkt3oD5N6eBwmmJ6rQ9KPhvSQKT_hM,251
209
+ future/moves/sys.py,sha256=HOMRX4Loim75FMbWawd3oEwuGNJR-ClMREEFkVpBsRs,132
210
+ future/moves/test/__init__.py,sha256=yB9F-fDQpzu1v8cBoKgIrL2ScUNqjlkqEztYrGVCQ-0,110
211
+ future/moves/test/__pycache__/__init__.cpython-38.pyc,,
212
+ future/moves/test/__pycache__/support.cpython-38.pyc,,
213
+ future/moves/test/support.py,sha256=6zGgTTXcERyBJIQ04-X-sAe781tVgLVHp3HzmQPy52g,259
214
+ future/moves/tkinter/__init__.py,sha256=jV9vDx3wRl0bsoclU8oSe-5SqHQ3YpCbStmqtXnq1p4,620
215
+ future/moves/tkinter/__pycache__/__init__.cpython-38.pyc,,
216
+ future/moves/tkinter/__pycache__/colorchooser.cpython-38.pyc,,
217
+ future/moves/tkinter/__pycache__/commondialog.cpython-38.pyc,,
218
+ future/moves/tkinter/__pycache__/constants.cpython-38.pyc,,
219
+ future/moves/tkinter/__pycache__/dialog.cpython-38.pyc,,
220
+ future/moves/tkinter/__pycache__/dnd.cpython-38.pyc,,
221
+ future/moves/tkinter/__pycache__/filedialog.cpython-38.pyc,,
222
+ future/moves/tkinter/__pycache__/font.cpython-38.pyc,,
223
+ future/moves/tkinter/__pycache__/messagebox.cpython-38.pyc,,
224
+ future/moves/tkinter/__pycache__/scrolledtext.cpython-38.pyc,,
225
+ future/moves/tkinter/__pycache__/simpledialog.cpython-38.pyc,,
226
+ future/moves/tkinter/__pycache__/tix.cpython-38.pyc,,
227
+ future/moves/tkinter/__pycache__/ttk.cpython-38.pyc,,
228
+ future/moves/tkinter/colorchooser.py,sha256=kprlmpRtvDbW5Gq43H1mi2KmNJ2kuzLQOba0a5EwDkU,333
229
+ future/moves/tkinter/commondialog.py,sha256=mdUbq1IZqOGaSA7_8R367IukDCsMfzXiVHrTQQpp7Z0,333
230
+ future/moves/tkinter/constants.py,sha256=0qRUrZLRPdVxueABL9KTzzEWEsk6xM1rOjxK6OHxXtA,324
231
+ future/moves/tkinter/dialog.py,sha256=ksp-zvs-_A90P9RNHS8S27f1k8f48zG2Bel2jwZV5y0,311
232
+ future/moves/tkinter/dnd.py,sha256=C_Ah0Urnyf2XKE5u-oP6mWi16RzMSXgMA1uhBSAwKY8,306
233
+ future/moves/tkinter/filedialog.py,sha256=RSJFDGOP2AJ4T0ZscJ2hyF9ssOWp9t_S_DtnOmT-WZ8,323
234
+ future/moves/tkinter/font.py,sha256=TXarflhJRxqepaRNSDw6JFUVGz5P1T1C4_uF9VRqj3w,309
235
+ future/moves/tkinter/messagebox.py,sha256=WJt4t83kLmr_UnpCWFuLoyazZr3wAUOEl6ADn3osoEA,327
236
+ future/moves/tkinter/scrolledtext.py,sha256=DRzN8aBAlDBUo1B2KDHzdpRSzXBfH4rOOz0iuHXbQcg,329
237
+ future/moves/tkinter/simpledialog.py,sha256=6MhuVhZCJV4XfPpPSUWKlDLLGEi0Y2ZlGQ9TbsmJFL0,329
238
+ future/moves/tkinter/tix.py,sha256=aNeOfbWSGmcN69UmEGf4tJ-QIxLT6SU5ynzm1iWgepA,302
239
+ future/moves/tkinter/ttk.py,sha256=rRrJpDjcP2gjQNukECu4F026P-CkW-3Ca2tN6Oia-Fw,302
240
+ future/moves/urllib/__init__.py,sha256=yB9F-fDQpzu1v8cBoKgIrL2ScUNqjlkqEztYrGVCQ-0,110
241
+ future/moves/urllib/__pycache__/__init__.cpython-38.pyc,,
242
+ future/moves/urllib/__pycache__/error.cpython-38.pyc,,
243
+ future/moves/urllib/__pycache__/parse.cpython-38.pyc,,
244
+ future/moves/urllib/__pycache__/request.cpython-38.pyc,,
245
+ future/moves/urllib/__pycache__/response.cpython-38.pyc,,
246
+ future/moves/urllib/__pycache__/robotparser.cpython-38.pyc,,
247
+ future/moves/urllib/error.py,sha256=gfrKzv-6W5OjzNIfjvJaQkxABRLym2KwjfKFXSdDB60,479
248
+ future/moves/urllib/parse.py,sha256=xLLUMIIB5MreCdYzRZ5zIRWrhTRCoMO8RZEH4WPFQDY,1045
249
+ future/moves/urllib/request.py,sha256=ttIzq60PwjRyrLQUGdAtfYvs4fziVwvcLe2Kw-hvE0g,3496
250
+ future/moves/urllib/response.py,sha256=ZEZML0FpbB--GIeBFPvSzbtlVJ6EsR4tCI4qB7D8sFQ,342
251
+ future/moves/urllib/robotparser.py,sha256=j24p6dMNzUpGZtT3BQxwRoE-F88iWmBpKgu0tRV61FQ,179
252
+ future/moves/winreg.py,sha256=2zNAG59QI7vFlCj7kqDh0JrAYTpexOnI55PEAIjYhqo,163
253
+ future/moves/xmlrpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
+ future/moves/xmlrpc/__pycache__/__init__.cpython-38.pyc,,
255
+ future/moves/xmlrpc/__pycache__/client.cpython-38.pyc,,
256
+ future/moves/xmlrpc/__pycache__/server.cpython-38.pyc,,
257
+ future/moves/xmlrpc/client.py,sha256=2PfnL5IbKVwdKP7C8B1OUviEtuBObwoH4pAPfvHIvQc,143
258
+ future/moves/xmlrpc/server.py,sha256=ESDXdpUgTKyeFmCDSnJmBp8zONjJklsRJOvy4OtaALc,143
259
+ future/standard_library/__init__.py,sha256=7paz9IsD5qv_tvk5Rre3YrlA2_2aS1FJfI7UlrzAtWY,27743
260
+ future/standard_library/__pycache__/__init__.cpython-38.pyc,,
261
+ future/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
262
+ future/tests/__pycache__/__init__.cpython-38.pyc,,
263
+ future/tests/__pycache__/base.cpython-38.pyc,,
264
+ future/tests/base.py,sha256=7LTAKHJgUxOwmffD1kgcErVt2VouKcldPnq4iruqk_k,19956
265
+ future/types/__init__.py,sha256=5fBxWqf_OTQ8jZ7k2TS34rFH14togeR488F4zBHIQ-s,6831
266
+ future/types/__pycache__/__init__.cpython-38.pyc,,
267
+ future/types/__pycache__/newbytes.cpython-38.pyc,,
268
+ future/types/__pycache__/newdict.cpython-38.pyc,,
269
+ future/types/__pycache__/newint.cpython-38.pyc,,
270
+ future/types/__pycache__/newlist.cpython-38.pyc,,
271
+ future/types/__pycache__/newmemoryview.cpython-38.pyc,,
272
+ future/types/__pycache__/newobject.cpython-38.pyc,,
273
+ future/types/__pycache__/newopen.cpython-38.pyc,,
274
+ future/types/__pycache__/newrange.cpython-38.pyc,,
275
+ future/types/__pycache__/newstr.cpython-38.pyc,,
276
+ future/types/newbytes.py,sha256=D_kNDD9sbNJir2cUxxePiAuw2OW5irxVnu55uHmuK9E,16303
277
+ future/types/newdict.py,sha256=2N7P44cWmWtiDHvlK5ir15mW492gg6uP2n65d5bsDy4,3100
278
+ future/types/newint.py,sha256=hJiv9qUDrjl1xkfzNFNLzafsRMPoFcRFceoivUzVIek,13286
279
+ future/types/newlist.py,sha256=-H5-fXodd-UQgTFnZBJdwE68CrgIL_jViYdv4w7q2rU,2284
280
+ future/types/newmemoryview.py,sha256=LnARgiKqQ2zLwwDZ3owu1atoonPQkOneWMfxJCwB4_o,712
281
+ future/types/newobject.py,sha256=AX_n8GwlDR2IY-xIwZCvu0Olj_Ca2aS57nlTihnFr-I,3358
282
+ future/types/newopen.py,sha256=lcRNHWZ1UjEn_0_XKis1ZA5U6l-4c-CHlC0WX1sY4NI,810
283
+ future/types/newrange.py,sha256=7sgJaRaC4WIUtZ40K-c1d5QWruyaCWGgTVFadKo8qYA,5294
284
+ future/types/newstr.py,sha256=e0brkurI0IK--4ToQEO4Cz1FECZav4CyUGMKxlrcmK4,15758
285
+ future/utils/__init__.py,sha256=wsvXsKx-DXZichQ10Rdml-CWMqS79RNNynmdvfISpCU,21828
286
+ future/utils/__pycache__/__init__.cpython-38.pyc,,
287
+ future/utils/__pycache__/surrogateescape.cpython-38.pyc,,
288
+ future/utils/surrogateescape.py,sha256=7u4V4XlW83P5YSAJS2f92YUF8vsWthsiTnmAshOJL_M,6097
289
+ libfuturize/__init__.py,sha256=CZA_KgvTQOPAY1_MrlJeQ6eMh2Eei4_KIv4JuyAkpfw,31
290
+ libfuturize/__pycache__/__init__.cpython-38.pyc,,
291
+ libfuturize/__pycache__/fixer_util.cpython-38.pyc,,
292
+ libfuturize/__pycache__/main.cpython-38.pyc,,
293
+ libfuturize/fixer_util.py,sha256=Zhms5G97l40pyG1krQM2lCp-TxnocBdJkB2AbkAFnKY,17494
294
+ libfuturize/fixes/__init__.py,sha256=5KEpUnjVsFCCsr_-zrikvJbLf9zslEJnFTH_5pBc33I,5236
295
+ libfuturize/fixes/__pycache__/__init__.cpython-38.pyc,,
296
+ libfuturize/fixes/__pycache__/fix_UserDict.cpython-38.pyc,,
297
+ libfuturize/fixes/__pycache__/fix_absolute_import.cpython-38.pyc,,
298
+ libfuturize/fixes/__pycache__/fix_add__future__imports_except_unicode_literals.cpython-38.pyc,,
299
+ libfuturize/fixes/__pycache__/fix_basestring.cpython-38.pyc,,
300
+ libfuturize/fixes/__pycache__/fix_bytes.cpython-38.pyc,,
301
+ libfuturize/fixes/__pycache__/fix_cmp.cpython-38.pyc,,
302
+ libfuturize/fixes/__pycache__/fix_division.cpython-38.pyc,,
303
+ libfuturize/fixes/__pycache__/fix_division_safe.cpython-38.pyc,,
304
+ libfuturize/fixes/__pycache__/fix_execfile.cpython-38.pyc,,
305
+ libfuturize/fixes/__pycache__/fix_future_builtins.cpython-38.pyc,,
306
+ libfuturize/fixes/__pycache__/fix_future_standard_library.cpython-38.pyc,,
307
+ libfuturize/fixes/__pycache__/fix_future_standard_library_urllib.cpython-38.pyc,,
308
+ libfuturize/fixes/__pycache__/fix_input.cpython-38.pyc,,
309
+ libfuturize/fixes/__pycache__/fix_metaclass.cpython-38.pyc,,
310
+ libfuturize/fixes/__pycache__/fix_next_call.cpython-38.pyc,,
311
+ libfuturize/fixes/__pycache__/fix_object.cpython-38.pyc,,
312
+ libfuturize/fixes/__pycache__/fix_oldstr_wrap.cpython-38.pyc,,
313
+ libfuturize/fixes/__pycache__/fix_order___future__imports.cpython-38.pyc,,
314
+ libfuturize/fixes/__pycache__/fix_print.cpython-38.pyc,,
315
+ libfuturize/fixes/__pycache__/fix_print_with_import.cpython-38.pyc,,
316
+ libfuturize/fixes/__pycache__/fix_raise.cpython-38.pyc,,
317
+ libfuturize/fixes/__pycache__/fix_remove_old__future__imports.cpython-38.pyc,,
318
+ libfuturize/fixes/__pycache__/fix_unicode_keep_u.cpython-38.pyc,,
319
+ libfuturize/fixes/__pycache__/fix_unicode_literals_import.cpython-38.pyc,,
320
+ libfuturize/fixes/__pycache__/fix_xrange_with_import.cpython-38.pyc,,
321
+ libfuturize/fixes/fix_UserDict.py,sha256=jL4jXnGaUQTkG8RKfGXbU_HVTkB3MWZMQwUkqMAjB6I,3840
322
+ libfuturize/fixes/fix_absolute_import.py,sha256=vkrF2FyQR5lSz2WmdqywzkEJVTC0eq4gh_REWBKHh7w,3140
323
+ libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py,sha256=Fr219VAzR8KWXc2_bfiqLl10EgxAWjL6cI3Mowt--VU,662
324
+ libfuturize/fixes/fix_basestring.py,sha256=bHkKuMzhr5FMXwjXlMOjsod4S3rQkVdbzhoWV4-tl3Y,394
325
+ libfuturize/fixes/fix_bytes.py,sha256=AhzOJes6EnPwgzboDjvURANbWKqciG6ZGaYW07PYQK8,685
326
+ libfuturize/fixes/fix_cmp.py,sha256=Blq_Z0IGkYiKS83QzZ5wUgpJyZfQiZoEsWJ1VPyXgFY,701
327
+ libfuturize/fixes/fix_division.py,sha256=gnrAi7stquiVUyi_De1H8q--43iQaSUX0CjnOmQ6O2w,228
328
+ libfuturize/fixes/fix_division_safe.py,sha256=Y_HUfQJAxRClXkcfqWP5SFCsRYZOsLUsNjLXlGOA3cQ,3292
329
+ libfuturize/fixes/fix_execfile.py,sha256=I5AcJ6vPZ7i70TChaq9inxqnZ4C04-yJyfAItGa8E3c,921
330
+ libfuturize/fixes/fix_future_builtins.py,sha256=QBCRpD9XA7tbtfP4wmOF2DXquB4lq-eupkQj-QAxp0s,2027
331
+ libfuturize/fixes/fix_future_standard_library.py,sha256=FVtflFt38efHe_SEX6k3m6IYAtKWjA4rAPZrlCv6yA0,733
332
+ libfuturize/fixes/fix_future_standard_library_urllib.py,sha256=Rf81XcAXA-vwNvrhskf5sLExbR--Wkr5fiUcMYGAKzs,1001
333
+ libfuturize/fixes/fix_input.py,sha256=bhaPNtMrZNbjWIDQCR7Iue5BxBj4rf0RJQ9_jiwvb-s,687
334
+ libfuturize/fixes/fix_metaclass.py,sha256=GLB76wbuyUVciDgW9bgNNOBEnLeS_AR-fKABcPBZk6M,9568
335
+ libfuturize/fixes/fix_next_call.py,sha256=01STG86Av9o5QcpQDJ6UbPhvxt9kKrkatiPeddXRgvA,3158
336
+ libfuturize/fixes/fix_object.py,sha256=qalFIjn0VTWXG5sGOOoCvO65omjX5_9d40SUpwUjBdw,407
337
+ libfuturize/fixes/fix_oldstr_wrap.py,sha256=UCR6Q2l-pVqJSrRTnQAWMlaqBoX7oX1VpG_w6Q0XcyY,1214
338
+ libfuturize/fixes/fix_order___future__imports.py,sha256=ACUCw5NEGWvj6XA9rNj8BYha3ktxLvkM5Ssh5cyV644,829
339
+ libfuturize/fixes/fix_print.py,sha256=92s1w2t9SynA3Y1_85-lexSBbgEWJM6lBrhCxVacfDc,3384
340
+ libfuturize/fixes/fix_print_with_import.py,sha256=hVWn70Q1DPMUiHMyEqgUx-6sM1AylLj78v9pMc4LFw8,735
341
+ libfuturize/fixes/fix_raise.py,sha256=mEXpM9sS6tenMmxayfqM-Kp9gUvaztTY61vFaqyMUuo,3884
342
+ libfuturize/fixes/fix_remove_old__future__imports.py,sha256=j4EC1KEVgXhuQAqhYHnAruUjW6uczPjV_fTCSOLMuAw,851
343
+ libfuturize/fixes/fix_unicode_keep_u.py,sha256=M8fcFxHeFnWVOKoQRpkMsnpd9qmUFubI2oFhO4ZPk7A,779
344
+ libfuturize/fixes/fix_unicode_literals_import.py,sha256=wq-hb-9Yx3Az4ol-ylXZJPEDZ81EaPZeIy5VvpA0CEY,367
345
+ libfuturize/fixes/fix_xrange_with_import.py,sha256=f074qStjMz3OtLjt1bKKZSxQnRbbb7HzEbqHt9wgqdw,479
346
+ libfuturize/main.py,sha256=feICmcv0dzWhutvwz0unnIVxusbSlQZFDaxObkHebs8,13733
347
+ libpasteurize/__init__.py,sha256=CZA_KgvTQOPAY1_MrlJeQ6eMh2Eei4_KIv4JuyAkpfw,31
348
+ libpasteurize/__pycache__/__init__.cpython-38.pyc,,
349
+ libpasteurize/__pycache__/main.cpython-38.pyc,,
350
+ libpasteurize/fixes/__init__.py,sha256=ccdv-2MGjQMbq8XuEZBndHmbzGRrZnabksjXZLUv044,3719
351
+ libpasteurize/fixes/__pycache__/__init__.cpython-38.pyc,,
352
+ libpasteurize/fixes/__pycache__/feature_base.cpython-38.pyc,,
353
+ libpasteurize/fixes/__pycache__/fix_add_all__future__imports.cpython-38.pyc,,
354
+ libpasteurize/fixes/__pycache__/fix_add_all_future_builtins.cpython-38.pyc,,
355
+ libpasteurize/fixes/__pycache__/fix_add_future_standard_library_import.cpython-38.pyc,,
356
+ libpasteurize/fixes/__pycache__/fix_annotations.cpython-38.pyc,,
357
+ libpasteurize/fixes/__pycache__/fix_division.cpython-38.pyc,,
358
+ libpasteurize/fixes/__pycache__/fix_features.cpython-38.pyc,,
359
+ libpasteurize/fixes/__pycache__/fix_fullargspec.cpython-38.pyc,,
360
+ libpasteurize/fixes/__pycache__/fix_future_builtins.cpython-38.pyc,,
361
+ libpasteurize/fixes/__pycache__/fix_getcwd.cpython-38.pyc,,
362
+ libpasteurize/fixes/__pycache__/fix_imports.cpython-38.pyc,,
363
+ libpasteurize/fixes/__pycache__/fix_imports2.cpython-38.pyc,,
364
+ libpasteurize/fixes/__pycache__/fix_kwargs.cpython-38.pyc,,
365
+ libpasteurize/fixes/__pycache__/fix_memoryview.cpython-38.pyc,,
366
+ libpasteurize/fixes/__pycache__/fix_metaclass.cpython-38.pyc,,
367
+ libpasteurize/fixes/__pycache__/fix_newstyle.cpython-38.pyc,,
368
+ libpasteurize/fixes/__pycache__/fix_next.cpython-38.pyc,,
369
+ libpasteurize/fixes/__pycache__/fix_printfunction.cpython-38.pyc,,
370
+ libpasteurize/fixes/__pycache__/fix_raise.cpython-38.pyc,,
371
+ libpasteurize/fixes/__pycache__/fix_raise_.cpython-38.pyc,,
372
+ libpasteurize/fixes/__pycache__/fix_throw.cpython-38.pyc,,
373
+ libpasteurize/fixes/__pycache__/fix_unpacking.cpython-38.pyc,,
374
+ libpasteurize/fixes/feature_base.py,sha256=v7yLjBDBUPeNUc-YHGGlIsJDOQzFAM4Vo0RN5F1JHVU,1723
375
+ libpasteurize/fixes/fix_add_all__future__imports.py,sha256=mHet1LgbHn9GfgCYGNZXKo-rseDWreAvUcAjZwdgeTE,676
376
+ libpasteurize/fixes/fix_add_all_future_builtins.py,sha256=scfkY-Sz5j0yDtLYls2ENOcqEMPVxeDm9gFYYPINPB8,1269
377
+ libpasteurize/fixes/fix_add_future_standard_library_import.py,sha256=thTRbkBzy_SJjZ0bJteTp0sBTx8Wr69xFakH4styf7Y,663
378
+ libpasteurize/fixes/fix_annotations.py,sha256=VT_AorKY9AYWYZUZ17_CeUrJlEA7VGkwVLDQlwD1Bxo,1581
379
+ libpasteurize/fixes/fix_division.py,sha256=_TD_c5KniAYqEm11O7NJF0v2WEhYSNkRGcKG_94ZOas,904
380
+ libpasteurize/fixes/fix_features.py,sha256=NZn0n34_MYZpLNwyP1Tf51hOiN58Rg7A8tA9pK1S8-c,2675
381
+ libpasteurize/fixes/fix_fullargspec.py,sha256=VlZuIU6QNrClmRuvC4mtLICL3yMCi-RcGCnS9fD4b-Q,438
382
+ libpasteurize/fixes/fix_future_builtins.py,sha256=SlCK9I9u05m19Lr1wxlJxF8toZ5yu0yXBeDLxUN9_fw,1450
383
+ libpasteurize/fixes/fix_getcwd.py,sha256=uebvTvFboLqsROFCwdnzoP6ThziM0skz9TDXHoJcFsQ,873
384
+ libpasteurize/fixes/fix_imports.py,sha256=U4lIs_5Xp1qqM8mN72ieDkkIdiyALZFyCZsRC8ZmXlM,4944
385
+ libpasteurize/fixes/fix_imports2.py,sha256=bs2V5Yv0v_8xLx-lNj9kNEAK2dLYXUXkZ2hxECg01CU,8580
386
+ libpasteurize/fixes/fix_kwargs.py,sha256=NB_Ap8YJk-9ncoJRbOiPY_VMIigFgVB8m8AuY29DDhE,5991
387
+ libpasteurize/fixes/fix_memoryview.py,sha256=Fwayx_ezpr22tbJ0-QrKdJ-FZTpU-m7y78l1h_N4xxc,551
388
+ libpasteurize/fixes/fix_metaclass.py,sha256=IcE2KjaDG8jUR3FYXECzOC_cr2pr5r95W1NTbMrK8Wc,3260
389
+ libpasteurize/fixes/fix_newstyle.py,sha256=78sazKOHm9DUoMyW4VdvQpMXZhicbXzorVPRhBpSUrM,888
390
+ libpasteurize/fixes/fix_next.py,sha256=VHqcyORRNVqKJ51jJ1OkhwxHuXRgp8qaldyqcMvA4J0,1233
391
+ libpasteurize/fixes/fix_printfunction.py,sha256=NDIfqVmUJBG3H9E6nrnN0cWZK8ch9pL4F-nMexdsa38,401
392
+ libpasteurize/fixes/fix_raise.py,sha256=zQ_AcMsGmCbtKMgrxZGcHLYNscw6tqXFvHQxgqtNbU8,1099
393
+ libpasteurize/fixes/fix_raise_.py,sha256=9STp633frUfYASjYzqhwxx_MXePNmMhfJClowRj8FLY,1225
394
+ libpasteurize/fixes/fix_throw.py,sha256=_ZREVre-WttUvk4sWjrqUNqm9Q1uFaATECN0_-PXKbk,835
395
+ libpasteurize/fixes/fix_unpacking.py,sha256=eMqRe44Nfq8lo0YFL9oKW75dGARmBSmklj4BCS_q1Lo,5946
396
+ libpasteurize/main.py,sha256=dVHYTQQeJonuOFDNrenJZl-rKHgOQKRMPP1OqnJogWQ,8186
397
+ past/__init__.py,sha256=wIiXaAvXl3svDi-fzuy6HDD0VsuCVr4cnqnCr8XINGI,2918
398
+ past/__pycache__/__init__.cpython-38.pyc,,
399
+ past/builtins/__init__.py,sha256=7j_4OsUlN6q2eKr14do7mRQ1GwXRoXAMUR0A1fJpAls,1805
400
+ past/builtins/__pycache__/__init__.cpython-38.pyc,,
401
+ past/builtins/__pycache__/misc.cpython-38.pyc,,
402
+ past/builtins/__pycache__/noniterators.cpython-38.pyc,,
403
+ past/builtins/misc.py,sha256=nw62HVSxuAgT-Q2lD3lmgRB9zmFXopS14dZHEv5xpDQ,2627
404
+ past/builtins/noniterators.py,sha256=LtdELnd7KyYdXg7GkW25cgkEPUC0ggZ5AYMtDe9N95I,9370
405
+ past/translation/__init__.py,sha256=j2e6mLeK74KEICqH6P_-tpKqSNZoMwip2toThhSmKpU,17646
406
+ past/translation/__pycache__/__init__.cpython-38.pyc,,
407
+ past/types/__init__.py,sha256=RyJlgqg9uJ8oF-kJT9QlfhfdmhiMh3fShmtvd2CQycY,879
408
+ past/types/__pycache__/__init__.cpython-38.pyc,,
409
+ past/types/__pycache__/basestring.cpython-38.pyc,,
410
+ past/types/__pycache__/olddict.cpython-38.pyc,,
411
+ past/types/__pycache__/oldstr.cpython-38.pyc,,
412
+ past/types/basestring.py,sha256=qrImcr24wvdDCMvF9x0Tyx8S1lCt6GIwRvzuAmvg_Tg,728
413
+ past/types/olddict.py,sha256=0YtffZ55VY6AyQ_rwu4DZ4vcRsp6dz-dQzczeyN8hLk,2721
414
+ past/types/oldstr.py,sha256=J2sJPC5jWEdpqXPcFwJFNDKn51TKhi86PsLFmJtQr-M,4332
415
+ past/utils/__init__.py,sha256=e8l1sOfdiDJ3dkckBWLNWvC1ahC5BX5haHC2TGdNgA8,2633
416
+ past/utils/__pycache__/__init__.cpython-38.pyc,,
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/REQUESTED ADDED
File without changes
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.36.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/future-0.18.2.dist-info/top_level.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ future
2
+ libfuturize
3
+ libpasteurize
4
+ past
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/__init__.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+
3
+ # Source of truth for Hydra's version
4
+ __version__ = "1.2.0"
5
+ from hydra import utils
6
+ from hydra.errors import MissingConfigException
7
+ from hydra.main import main
8
+ from hydra.types import TaskFunction
9
+
10
+ from .compose import compose
11
+ from .initialize import initialize, initialize_config_dir, initialize_config_module
12
+
13
+ __all__ = [
14
+ "__version__",
15
+ "MissingConfigException",
16
+ "main",
17
+ "utils",
18
+ "TaskFunction",
19
+ "compose",
20
+ "initialize",
21
+ "initialize_config_module",
22
+ "initialize_config_dir",
23
+ ]
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/compose.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from textwrap import dedent
3
+ from typing import List, Optional
4
+
5
+ from omegaconf import DictConfig, OmegaConf, open_dict
6
+
7
+ from hydra import version
8
+ from hydra.core.global_hydra import GlobalHydra
9
+ from hydra.types import RunMode
10
+
11
+ from ._internal.deprecation_warning import deprecation_warning
12
+
13
+
14
+ def compose(
15
+ config_name: Optional[str] = None,
16
+ overrides: List[str] = [],
17
+ return_hydra_config: bool = False,
18
+ strict: Optional[bool] = None,
19
+ ) -> DictConfig:
20
+ """
21
+ :param config_name: the name of the config
22
+ (usually the file name without the .yaml extension)
23
+ :param overrides: list of overrides for config file
24
+ :param return_hydra_config: True to return the hydra config node in the result
25
+ :param strict: DEPRECATED. If false, returned config has struct mode disabled.
26
+ :return: the composed config
27
+ """
28
+ assert (
29
+ GlobalHydra().is_initialized()
30
+ ), "GlobalHydra is not initialized, use @hydra.main() or call one of the hydra initialization methods first"
31
+
32
+ gh = GlobalHydra.instance()
33
+ assert gh.hydra is not None
34
+ cfg = gh.hydra.compose_config(
35
+ config_name=config_name,
36
+ overrides=overrides,
37
+ run_mode=RunMode.RUN,
38
+ from_shell=False,
39
+ with_log_configuration=False,
40
+ )
41
+ assert isinstance(cfg, DictConfig)
42
+
43
+ if not return_hydra_config:
44
+ if "hydra" in cfg:
45
+ with open_dict(cfg):
46
+ del cfg["hydra"]
47
+
48
+ if strict is not None:
49
+ if version.base_at_least("1.2"):
50
+ raise TypeError("got an unexpected 'strict' argument")
51
+ else:
52
+ deprecation_warning(
53
+ dedent(
54
+ """
55
+ The strict flag in the compose API is deprecated.
56
+ See https://hydra.cc/docs/upgrades/0.11_to_1.0/strict_mode_flag_deprecated for more info.
57
+ """
58
+ )
59
+ )
60
+ OmegaConf.set_struct(cfg, strict)
61
+
62
+ return cfg
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/errors.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from typing import Optional, Sequence
3
+
4
+
5
+ class HydraException(Exception):
6
+ ...
7
+
8
+
9
+ class CompactHydraException(HydraException):
10
+ ...
11
+
12
+
13
+ class OverrideParseException(CompactHydraException):
14
+ def __init__(self, override: str, message: str) -> None:
15
+ super(OverrideParseException, self).__init__(message)
16
+ self.override = override
17
+ self.message = message
18
+
19
+
20
+ class InstantiationException(CompactHydraException):
21
+ ...
22
+
23
+
24
+ class ConfigCompositionException(CompactHydraException):
25
+ ...
26
+
27
+
28
+ class SearchPathException(CompactHydraException):
29
+ ...
30
+
31
+
32
+ class MissingConfigException(IOError, ConfigCompositionException):
33
+ def __init__(
34
+ self,
35
+ message: str,
36
+ missing_cfg_file: Optional[str],
37
+ options: Optional[Sequence[str]] = None,
38
+ ) -> None:
39
+ super(MissingConfigException, self).__init__(message)
40
+ self.missing_cfg_file = missing_cfg_file
41
+ self.options = options
42
+
43
+
44
+ class HydraDeprecationError(HydraException):
45
+ ...
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/initialize.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import copy
3
+ import os
4
+ from textwrap import dedent
5
+ from typing import Any, Optional
6
+
7
+ from hydra import version
8
+ from hydra._internal.deprecation_warning import deprecation_warning
9
+ from hydra._internal.hydra import Hydra
10
+ from hydra._internal.utils import (
11
+ create_config_search_path,
12
+ detect_calling_file_or_module_from_stack_frame,
13
+ detect_task_name,
14
+ )
15
+ from hydra.core.global_hydra import GlobalHydra
16
+ from hydra.core.singleton import Singleton
17
+ from hydra.errors import HydraException
18
+
19
+
20
+ def get_gh_backup() -> Any:
21
+ if GlobalHydra in Singleton._instances:
22
+ return copy.deepcopy(Singleton._instances[GlobalHydra])
23
+ else:
24
+ return None
25
+
26
+
27
+ def restore_gh_from_backup(_gh_backup: Any) -> Any:
28
+ if _gh_backup is None:
29
+ del Singleton._instances[GlobalHydra]
30
+ else:
31
+ Singleton._instances[GlobalHydra] = _gh_backup
32
+
33
+
34
+ _UNSPECIFIED_: Any = object()
35
+
36
+
37
+ class initialize:
38
+ """
39
+ Initializes Hydra and add the config_path to the config search path.
40
+ config_path is relative to the parent of the caller.
41
+ Hydra detects the caller type automatically at runtime.
42
+
43
+ Supported callers:
44
+ - Python scripts
45
+ - Python modules
46
+ - Unit tests
47
+ - Jupyter notebooks.
48
+ :param config_path: path relative to the parent of the caller
49
+ :param job_name: the value for hydra.job.name (By default it is automatically detected based on the caller)
50
+ :param caller_stack_depth: stack depth of the caller, defaults to 1 (direct caller).
51
+ """
52
+
53
+ def __init__(
54
+ self,
55
+ config_path: Optional[str] = _UNSPECIFIED_,
56
+ version_base: Optional[str] = _UNSPECIFIED_,
57
+ job_name: Optional[str] = None,
58
+ caller_stack_depth: int = 1,
59
+ ) -> None:
60
+ self._gh_backup = get_gh_backup()
61
+
62
+ version.setbase(version_base)
63
+
64
+ if config_path is _UNSPECIFIED_:
65
+ if version.base_at_least("1.2"):
66
+ config_path = None
67
+ elif version_base is _UNSPECIFIED_:
68
+ url = "https://hydra.cc/docs/next/upgrades/1.0_to_1.1/changes_to_hydra_main_config_path"
69
+ deprecation_warning(
70
+ message=dedent(
71
+ f"""\
72
+ config_path is not specified in hydra.initialize().
73
+ See {url} for more information."""
74
+ ),
75
+ stacklevel=2,
76
+ )
77
+ config_path = "."
78
+ else:
79
+ config_path = "."
80
+
81
+ if config_path is not None and os.path.isabs(config_path):
82
+ raise HydraException("config_path in initialize() must be relative")
83
+ calling_file, calling_module = detect_calling_file_or_module_from_stack_frame(
84
+ caller_stack_depth + 1
85
+ )
86
+ if job_name is None:
87
+ job_name = detect_task_name(
88
+ calling_file=calling_file, calling_module=calling_module
89
+ )
90
+
91
+ Hydra.create_main_hydra_file_or_module(
92
+ calling_file=calling_file,
93
+ calling_module=calling_module,
94
+ config_path=config_path,
95
+ job_name=job_name,
96
+ )
97
+
98
+ def __enter__(self, *args: Any, **kwargs: Any) -> None:
99
+ ...
100
+
101
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
102
+ restore_gh_from_backup(self._gh_backup)
103
+
104
+ def __repr__(self) -> str:
105
+ return "hydra.initialize()"
106
+
107
+
108
+ class initialize_config_module:
109
+ """
110
+ Initializes Hydra and add the config_module to the config search path.
111
+ The config module must be importable (an __init__.py must exist at its top level)
112
+ :param config_module: absolute module name, for example "foo.bar.conf".
113
+ :param job_name: the value for hydra.job.name (default is 'app')
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ config_module: str,
119
+ version_base: Optional[str] = _UNSPECIFIED_,
120
+ job_name: str = "app",
121
+ ):
122
+ self._gh_backup = get_gh_backup()
123
+
124
+ version.setbase(version_base)
125
+
126
+ Hydra.create_main_hydra_file_or_module(
127
+ calling_file=None,
128
+ calling_module=f"{config_module}.{job_name}",
129
+ config_path=None,
130
+ job_name=job_name,
131
+ )
132
+
133
+ def __enter__(self, *args: Any, **kwargs: Any) -> None:
134
+ ...
135
+
136
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
137
+ restore_gh_from_backup(self._gh_backup)
138
+
139
+ def __repr__(self) -> str:
140
+ return "hydra.initialize_config_module()"
141
+
142
+
143
+ class initialize_config_dir:
144
+ """
145
+ Initializes Hydra and add an absolute config dir to the to the config search path.
146
+ The config_dir is always a path on the file system and is must be an absolute path.
147
+ Relative paths will result in an error.
148
+ :param config_dir: absolute file system path
149
+ :param job_name: the value for hydra.job.name (default is 'app')
150
+ """
151
+
152
+ def __init__(
153
+ self,
154
+ config_dir: str,
155
+ version_base: Optional[str] = _UNSPECIFIED_,
156
+ job_name: str = "app",
157
+ ) -> None:
158
+ self._gh_backup = get_gh_backup()
159
+
160
+ version.setbase(version_base)
161
+
162
+ # Relative here would be interpreted as relative to cwd, which - depending on when it run
163
+ # may have unexpected meaning. best to force an absolute path to avoid confusion.
164
+ # Can consider using hydra.utils.to_absolute_path() to convert it at a future point if there is demand.
165
+ if not os.path.isabs(config_dir):
166
+ raise HydraException(
167
+ "initialize_config_dir() requires an absolute config_dir as input"
168
+ )
169
+ csp = create_config_search_path(search_path_dir=config_dir)
170
+ Hydra.create_main_hydra2(task_name=job_name, config_search_path=csp)
171
+
172
+ def __enter__(self, *args: Any, **kwargs: Any) -> None:
173
+ ...
174
+
175
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
176
+ restore_gh_from_backup(self._gh_backup)
177
+
178
+ def __repr__(self) -> str:
179
+ return "hydra.initialize_config_dir()"
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/main.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import copy
3
+ import functools
4
+ import pickle
5
+ import warnings
6
+ from pathlib import Path
7
+ from textwrap import dedent
8
+ from typing import Any, Callable, List, Optional
9
+
10
+ from omegaconf import DictConfig, open_dict, read_write
11
+
12
+ from . import version
13
+ from ._internal.deprecation_warning import deprecation_warning
14
+ from ._internal.utils import _run_hydra, get_args_parser
15
+ from .core.hydra_config import HydraConfig
16
+ from .core.utils import _flush_loggers, configure_log
17
+ from .types import TaskFunction
18
+
19
+ _UNSPECIFIED_: Any = object()
20
+
21
+
22
+ def _get_rerun_conf(file_path: str, overrides: List[str]) -> DictConfig:
23
+ msg = "Experimental rerun CLI option, other command line args are ignored."
24
+ warnings.warn(msg, UserWarning)
25
+ file = Path(file_path)
26
+ if not file.exists():
27
+ raise ValueError(f"File {file} does not exist!")
28
+
29
+ if len(overrides) > 0:
30
+ msg = "Config overrides are not supported as of now."
31
+ warnings.warn(msg, UserWarning)
32
+
33
+ with open(str(file), "rb") as input:
34
+ config = pickle.load(input) # nosec
35
+ configure_log(config.hydra.job_logging, config.hydra.verbose)
36
+ HydraConfig.instance().set_config(config)
37
+ task_cfg = copy.deepcopy(config)
38
+ with read_write(task_cfg):
39
+ with open_dict(task_cfg):
40
+ del task_cfg["hydra"]
41
+ assert isinstance(task_cfg, DictConfig)
42
+ return task_cfg
43
+
44
+
45
+ def main(
46
+ config_path: Optional[str] = _UNSPECIFIED_,
47
+ config_name: Optional[str] = None,
48
+ version_base: Optional[str] = _UNSPECIFIED_,
49
+ ) -> Callable[[TaskFunction], Any]:
50
+ """
51
+ :param config_path: The config path, a directory relative to the declaring python file.
52
+ If config_path is None no directory is added to the Config search path.
53
+ :param config_name: The name of the config (usually the file name without the .yaml extension)
54
+ """
55
+
56
+ version.setbase(version_base)
57
+
58
+ if config_path is _UNSPECIFIED_:
59
+ if version.base_at_least("1.2"):
60
+ config_path = None
61
+ elif version_base is _UNSPECIFIED_:
62
+ url = "https://hydra.cc/docs/next/upgrades/1.0_to_1.1/changes_to_hydra_main_config_path"
63
+ deprecation_warning(
64
+ message=dedent(
65
+ f"""
66
+ config_path is not specified in @hydra.main().
67
+ See {url} for more information."""
68
+ ),
69
+ stacklevel=2,
70
+ )
71
+ config_path = "."
72
+ else:
73
+ config_path = "."
74
+
75
+ def main_decorator(task_function: TaskFunction) -> Callable[[], None]:
76
+ @functools.wraps(task_function)
77
+ def decorated_main(cfg_passthrough: Optional[DictConfig] = None) -> Any:
78
+ if cfg_passthrough is not None:
79
+ return task_function(cfg_passthrough)
80
+ else:
81
+ args_parser = get_args_parser()
82
+ args = args_parser.parse_args()
83
+ if args.experimental_rerun is not None:
84
+ cfg = _get_rerun_conf(args.experimental_rerun, args.overrides)
85
+ task_function(cfg)
86
+ _flush_loggers()
87
+ else:
88
+ # no return value from run_hydra() as it may sometime actually run the task_function
89
+ # multiple times (--multirun)
90
+ _run_hydra(
91
+ args=args,
92
+ args_parser=args_parser,
93
+ task_function=task_function,
94
+ config_path=config_path,
95
+ config_name=config_name,
96
+ )
97
+
98
+ return decorated_main
99
+
100
+ return main_decorator
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/py.typed ADDED
File without changes
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/types.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from dataclasses import dataclass
3
+ from enum import Enum
4
+ from typing import TYPE_CHECKING, Any, Callable
5
+
6
+ from omegaconf import MISSING
7
+
8
+ from hydra import version
9
+
10
+ from ._internal.deprecation_warning import deprecation_warning
11
+
12
+ TaskFunction = Callable[[Any], Any]
13
+
14
+
15
+ if TYPE_CHECKING:
16
+ from hydra._internal.callbacks import Callbacks
17
+ from hydra.core.config_loader import ConfigLoader
18
+
19
+
20
+ @dataclass
21
+ class HydraContext:
22
+ config_loader: "ConfigLoader"
23
+ callbacks: "Callbacks"
24
+
25
+
26
+ @dataclass
27
+ class TargetConf:
28
+ """
29
+ This class is going away in Hydra 1.2.
30
+ You should no longer extend it or annotate with it.
31
+ instantiate will work correctly if you pass in a DictConfig object or any dataclass that has the
32
+ _target_ attribute.
33
+ """
34
+
35
+ _target_: str = MISSING
36
+
37
+ def __post_init__(self) -> None:
38
+ if version.base_at_least("1.2"):
39
+ raise TypeError("TargetConf is unsupported since Hydra 1.2")
40
+ else:
41
+ msg = "\nTargetConf is deprecated since Hydra 1.1 and will be removed in Hydra 1.2."
42
+ deprecation_warning(message=msg)
43
+
44
+
45
+ class RunMode(Enum):
46
+ RUN = 1
47
+ MULTIRUN = 2
48
+
49
+
50
+ class ConvertMode(Enum):
51
+ """ConvertMode for instantiate, controls return type.
52
+
53
+ A config is either config or instance-like (`_target_` field).
54
+
55
+ If instance-like, instantiate resolves the callable (class or
56
+ function) and returns the result of the call on the rest of the
57
+ parameters.
58
+
59
+ If "none", config-like configs will be kept as is.
60
+
61
+ If "partial", config-like configs will be converted to native python
62
+ containers (list and dict), unless they are structured configs (
63
+ dataclasses or attr instances).
64
+
65
+ If "all", config-like configs will all be converted to native python
66
+ containers (list and dict).
67
+ """
68
+
69
+ # Use DictConfig/ListConfig
70
+ NONE = "none"
71
+ # Convert the OmegaConf config to primitive container, Structured Configs are preserved
72
+ PARTIAL = "partial"
73
+ # Fully convert the OmegaConf config to primitive containers (dict, list and primitives).
74
+ ALL = "all"
75
+
76
+ def __eq__(self, other: Any) -> Any:
77
+ if isinstance(other, ConvertMode):
78
+ return other.value == self.value
79
+ elif isinstance(other, str):
80
+ return other.upper() == self.name.upper()
81
+ else:
82
+ return NotImplemented
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/utils.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+
3
+ import logging.config
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Any, Callable
7
+
8
+ import hydra._internal.instantiate._instantiate2
9
+ import hydra.types
10
+ from hydra._internal.utils import _locate
11
+ from hydra.core.hydra_config import HydraConfig
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+ # Instantiation related symbols
16
+ instantiate = hydra._internal.instantiate._instantiate2.instantiate
17
+ call = instantiate
18
+ ConvertMode = hydra.types.ConvertMode
19
+
20
+
21
+ def get_class(path: str) -> type:
22
+ try:
23
+ cls = _locate(path)
24
+ if not isinstance(cls, type):
25
+ raise ValueError(
26
+ f"Located non-class of type '{type(cls).__name__}'"
27
+ + f" while loading '{path}'"
28
+ )
29
+ return cls
30
+ except Exception as e:
31
+ log.error(f"Error initializing class at {path}: {e}")
32
+ raise e
33
+
34
+
35
+ def get_method(path: str) -> Callable[..., Any]:
36
+ try:
37
+ obj = _locate(path)
38
+ if not callable(obj):
39
+ raise ValueError(
40
+ f"Located non-callable of type '{type(obj).__name__}'"
41
+ + f" while loading '{path}'"
42
+ )
43
+ cl: Callable[..., Any] = obj
44
+ return cl
45
+ except Exception as e:
46
+ log.error(f"Error getting callable at {path} : {e}")
47
+ raise e
48
+
49
+
50
+ # Alias for get_method
51
+ get_static_method = get_method
52
+
53
+
54
+ def get_original_cwd() -> str:
55
+ """
56
+ :return: the original working directory the Hydra application was launched from
57
+ """
58
+ if not HydraConfig.initialized():
59
+ raise ValueError(
60
+ "get_original_cwd() must only be used after HydraConfig is initialized"
61
+ )
62
+ ret = HydraConfig.get().runtime.cwd
63
+ assert ret is not None and isinstance(ret, str)
64
+ return ret
65
+
66
+
67
+ def to_absolute_path(path: str) -> str:
68
+ """
69
+ converts the specified path to be absolute path.
70
+ if the input path is relative, it's interpreted as relative to the original working directory
71
+ if it's absolute, it's returned as is
72
+ :param path: path to convert
73
+ :return:
74
+ """
75
+ p = Path(path)
76
+ if not HydraConfig.initialized():
77
+ base = Path(os.getcwd())
78
+ else:
79
+ base = Path(get_original_cwd())
80
+ if p.is_absolute():
81
+ ret = p
82
+ else:
83
+ ret = base / p
84
+ return str(ret)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/hydra/version.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+
3
+ # Source of truth for Hydra's version
4
+
5
+ from textwrap import dedent
6
+ from typing import Any, Optional
7
+
8
+ from packaging.version import Version
9
+
10
+ from . import __version__
11
+ from ._internal.deprecation_warning import deprecation_warning
12
+ from .core.singleton import Singleton
13
+ from .errors import HydraException
14
+
15
+ _UNSPECIFIED_: Any = object()
16
+
17
+ __compat_version__: Version = Version("1.1")
18
+
19
+
20
+ class VersionBase(metaclass=Singleton):
21
+ def __init__(self) -> None:
22
+ self.version_base: Optional[Version] = _UNSPECIFIED_
23
+
24
+ def setbase(self, version: "Version") -> None:
25
+ assert isinstance(
26
+ version, Version
27
+ ), f"Unexpected Version type : {type(version)}"
28
+ self.version_base = version
29
+
30
+ def getbase(self) -> Optional[Version]:
31
+ return self.version_base
32
+
33
+ @staticmethod
34
+ def instance(*args: Any, **kwargs: Any) -> "VersionBase":
35
+ return Singleton.instance(VersionBase, *args, **kwargs) # type: ignore
36
+
37
+ @staticmethod
38
+ def set_instance(instance: "VersionBase") -> None:
39
+ assert isinstance(instance, VersionBase)
40
+ Singleton._instances[VersionBase] = instance # type: ignore
41
+
42
+
43
+ def _get_version(ver: str) -> Version:
44
+ # Only consider major.minor as packaging will compare "1.2.0.dev2" < "1.2"
45
+ pver = Version(ver)
46
+ return Version(f"{pver.major}.{pver.minor}")
47
+
48
+
49
+ def base_at_least(ver: str) -> bool:
50
+ _version_base = VersionBase.instance().getbase()
51
+ if type(_version_base) is type(_UNSPECIFIED_):
52
+ VersionBase.instance().setbase(__compat_version__)
53
+ _version_base = __compat_version__
54
+ assert isinstance(_version_base, Version)
55
+ return _version_base >= _get_version(ver)
56
+
57
+
58
+ def getbase() -> Optional[Version]:
59
+ return VersionBase.instance().getbase()
60
+
61
+
62
+ def setbase(ver: Any) -> None:
63
+ if type(ver) is type(_UNSPECIFIED_):
64
+ deprecation_warning(
65
+ message=dedent(
66
+ f"""
67
+ The version_base parameter is not specified.
68
+ Please specify a compatability version level, or None.
69
+ Will assume defaults for version {__compat_version__}"""
70
+ ),
71
+ stacklevel=3,
72
+ )
73
+ _version_base = __compat_version__
74
+ elif ver is None:
75
+ _version_base = _get_version(__version__)
76
+ else:
77
+ _version_base = _get_version(ver)
78
+ if _version_base < __compat_version__:
79
+ raise HydraException(f'version_base must be >= "{__compat_version__}"')
80
+ VersionBase.instance().setbase(_version_base)