We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Bug] [v2.-4dev] dup-rows/-deep does not copy rows
I found that if I try and do a `dup-rows-deep` with a DirSheet I get the following error with no rows in the sheet (columns are correct and as expected).

This only appears to happen with a DirSheet, I just tried with a TextSheet which works as expected.

```
Traceback (most recent call last):
  File "/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/threads.py", line 201, in _toplevelTryFunc
    t.status = func(*args, **kwargs)
  File "/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/threads.py", line 80, in _async_deepcopy
    newlist.append(deepcopy(r))
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/copy.py", line 281, in _reconstruct
    if hasattr(y, '__setstate__'):
  File "/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/path.py", line 77, in __getattr__
    r = getattr(self._path, k)
  File "/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/path.py", line 77, in __getattr__
    r = getattr(self._path, k)
  File "/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/path.py", line 77, in __getattr__
    r = getattr(self._path, k)
  [Previous line repeated 492 more times]
RecursionError: maximum recursion depth exceeded
```
### Replicate:

1. Use v2.-4dev branch
2. Open a DirSheet with `vd .`
3. Do a deep copy of the sheet with `dup-rows-deep`

### Result:
A sheet with no rows, columns as expected for a DirSheet, and the error message above.

### Expected:
A deep copy of the original DirSheet with all rows (including those selected)

### cmdlog:
```
sheet	col	row	longname	input	keystrokes	comment
	SqliteSheet	header	set-option	0
	UsvSheet	delimiter	set-option	␞
	UsvSheet	row_delimiter	set-option	␟
	override	disp_date_fmt	set-option	%Y-%m-%d %H:%M:%S
			open-file	.	o
_files			dup-rows-deep		gz"	open duplicate sheet with deepcopy of all rows
```


Also, am I correct in understanding that if I make a deep copy, modifications I make to that copy should propagate to the original sheet? And this should include selecting/deselecting rows?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `visidata/path.py`
Content:
```
1 import os
2 import os.path
3 import sys
4 import pathlib
5 from urllib.parse import urlparse, urlunparse
6 
7 from visidata import *
8 
9 option('encoding', 'utf-8', 'encoding passed to codecs.open', replay=True)
10 option('encoding_errors', 'surrogateescape', 'encoding_errors passed to codecs.open', replay=True)
11 
12 @functools.lru_cache()
13 def vstat(path, force=False):
14     try:
15         return os.stat(path)
16     except Exception as e:
17         return None
18 
19 def filesize(path):
20     if hasattr(path, 'filesize') and path.filesize is not None:
21         return path.filesize
22     if path.fp or path.is_url():
23         return 0
24     st = path.stat() # vstat(path)
25     return st and st.st_size
26 
27 def modtime(path):
28     st = path.stat()
29     return st and st.st_mtime
30 
31 
32 class Path(os.PathLike):
33     'File and path-handling class, modeled on `pathlib.Path`.'
34     def __init__(self, given, fp=None, lines=None, filesize=None):
35         # Resolve pathname shell variables and ~userdir
36         self.given = os.path.expandvars(os.path.expanduser(given))
37         self.fp = fp
38         self.lines = lines or []  # shared among all RepeatFile instances
39         self.filesize = filesize
40         self.rfile = None
41 
42     @functools.lru_cache()
43     def stat(self, force=False):
44         return self._path.stat()
45 
46     @property
47     def given(self):
48         return self._given
49 
50     @given.setter
51     def given(self, given):
52         self._given = given
53         if isinstance(given, os.PathLike):
54             self._path = given
55         else:
56             self._path = pathlib.Path(given)
57 
58         self.ext = self.suffix[1:]
59         if self.suffix:
60             self.name = self._path.name[:-len(self.suffix)]
61         else:
62             self.name = self._path.name
63 
64         # check if file is compressed
65         if self.suffix in ['.gz', '.bz2', '.xz']:
66             self.compression = self.ext
67             uncompressedpath = Path(self.given[:-len(self.suffix)])
68             self.name = uncompressedpath.name
69             self.ext = uncompressedpath.ext
70         else:
71             self.compression = None
72 
73     def __getattr__(self, k):
74         if hasattr(self.__dict__, k):
75             r = getattr(self.__dict__, k)
76         else:
77             r = getattr(self._path, k)
78         if isinstance(r, pathlib.Path):
79             return Path(r)
80         return r
81 
82     def __fspath__(self):
83         return self._path.__fspath__()
84 
85     def __lt__(self, a):
86         return self._path.__lt__(a)
87 
88     def __truediv__(self, a):
89         return Path(self._path.__truediv__(a))
90 
91     def open_text(self, mode='rt'):
92         # rfile makes a single-access fp reusable
93 
94         if self.rfile:
95             return self.rfile
96 
97         if self.fp:
98             self.rfile = RepeatFile(fp=self.fp)
99             return self.rfile
100 
101         if 't' not in mode:
102             mode += 't'
103 
104         if self.given == '-':
105             if 'r' in mode:
106                 return vd._stdin
107             elif 'w' in mode or 'a' in mode:
108                 # convert 'a' to 'w' for stdout: https://bugs.python.org/issue27805
109                 return open(os.dup(vd._stdout.fileno()), 'wt')
110             else:
111                 error('invalid mode "%s" for Path.open_text()' % mode)
112                 return sys.stderr
113 
114         return self.open(mode=mode, encoding=options.encoding, errors=options.encoding_errors)
115 
116     def read_text(self, *args):
117         if self.lines:
118             return RepeatFile(iter_lines=self.lines).read()
119         elif self.fp:
120             return self.fp.read()
121         else:
122             return self._path.read_text(*args)
123 
124     def open(self, *args, **kwargs):
125         fn = self
126         if self.compression == 'gz':
127             import gzip
128             return gzip.open(fn, *args, **kwargs)
129         elif self.compression == 'bz2':
130             import bz2
131             return bz2.open(fn, *args, **kwargs)
132         elif self.compression == 'xz':
133             import lzma
134             return lzma.open(fn, *args, **kwargs)
135         else:
136             return self._path.open(*args, **kwargs)
137 
138     def __iter__(self):
139         with Progress(total=filesize(self)) as prog:
140             for i, line in enumerate(self.open_text()):
141                 prog.addProgress(len(line))
142                 yield line[:-1]
143 
144     def open_bytes(self, mode='rb'):
145         if 'b' not in mode:
146             mode += 'b'
147         return self.open(mode=mode)
148 
149     def read_bytes(self):
150         with self.open(mode='rb') as fp:
151             return fp.read()
152 
153     def is_url(self):
154         return '://' in self.given
155 
156     def __str__(self):
157         if self.is_url():
158             return self.given
159         return str(self._path)
160 
161     @functools.lru_cache()
162     def stat(self, force=False):
163         try:
164             if not self.is_url():
165                 return self._path.stat()
166         except Exception as e:
167             return None
168 
169     def exists(self):
170         if self.fp or self.is_url():
171             return True
172         return self._path.exists()
173 
174     @property
175     def scheme(self):
176         if self.is_url():
177             return urlparse(self.given).scheme
178 
179     def with_name(self, name):
180         if self.is_url():
181             urlparts = list(urlparse(self.given))
182             urlparts[2] = '/'.join(Path(urlparts[2])._parts[1:-1] + [name])
183             return Path(urlunparse(urlparts))
184         else:
185             return Path(self._from_parsed_parts(self._drv, self._root, self._parts[:-1] + [name]))
186 
187 
188 class RepeatFile:
189     def __init__(self, *, fp=None, iter_lines=None):
190         'Provide either fp or iter_lines, and lines will be filled from it.'
191         self.fp = fp
192         self.iter_lines = iter_lines
193         self.lines = []
194         self.iter = RepeatFileIter(self)
195 
196     def __enter__(self):
197         self.iter = RepeatFileIter(self)
198         return self
199 
200     def __exit__(self, a,b,c):
201         pass
202 
203     def read(self, n=None):
204         r = ''
205         if n is None:
206             n = 10**12  # some too huge number
207         while len(r) < n:
208             try:
209                 s = next(self.iter)
210                 r += s + '\n'
211                 n += len(r)
212             except StopIteration:
213                 break  # end of file
214         return r
215 
216     def seek(self, n):
217         assert n == 0, 'RepeatFile can only seek to beginning'
218         self.iter = RepeatFileIter(self)
219 
220     def __iter__(self):
221         return RepeatFileIter(self)
222 
223     def __next__(self):
224         return next(self.iter)
225 
226     def exists(self):
227         return True
228 
229 
230 class RepeatFileIter:
231     def __init__(self, rf):
232         self.rf = rf
233         self.nextIndex = 0
234 
235     def __iter__(self):
236         return RepeatFileIter(self.rf)
237 
238     def __next__(self):
239         if self.nextIndex < len(self.rf.lines):
240             r = self.rf.lines[self.nextIndex]
241         elif self.rf.iter_lines:
242             try:
243                 r = next(self.rf.iter_lines)
244                 self.rf.lines.append(r)
245             except StopIteration:
246                 self.rf.iter_lines = None
247                 raise
248         elif self.rf.fp:
249             try:
250                 r = next(self.rf.fp)
251                 self.rf.lines.append(r)
252             except StopIteration:
253                 self.rf.fp = None
254                 raise
255         else:
256             raise StopIteration()
257 
258 
259         self.nextIndex += 1
260         return r
261
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/visidata/path.py b/visidata/path.py
--- a/visidata/path.py
+++ b/visidata/path.py
@@ -74,7 +74,10 @@
         if hasattr(self.__dict__, k):
             r = getattr(self.__dict__, k)
         else:
-            r = getattr(self._path, k)
+            if self.__dict__.get('_path', None) is not None:
+                r = getattr(self._path, k)
+            else:
+                raise AttributeError(k)
         if isinstance(r, pathlib.Path):
             return Path(r)
         return r

{"golden_diff": "diff --git a/visidata/path.py b/visidata/path.py\n--- a/visidata/path.py\n+++ b/visidata/path.py\n@@ -74,7 +74,10 @@\n         if hasattr(self.__dict__, k):\n             r = getattr(self.__dict__, k)\n         else:\n-            r = getattr(self._path, k)\n+            if self.__dict__.get('_path', None) is not None:\n+                r = getattr(self._path, k)\n+            else:\n+                raise AttributeError(k)\n         if isinstance(r, pathlib.Path):\n             return Path(r)\n         return r\n", "issue": "[Bug] [v2.-4dev] dup-rows/-deep does not copy rows\nI found that if I try and do a `dup-rows-deep` with a DirSheet I get the following error with no rows in the sheet (columns are correct and as expected).\r\n\r\nThis only appears to happen with a DirSheet, I just tried with a TextSheet which works as expected.\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/threads.py\", line 201, in _toplevelTryFunc\r\n    t.status = func(*args, **kwargs)\r\n  File \"/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/threads.py\", line 80, in _async_deepcopy\r\n    newlist.append(deepcopy(r))\r\n  File \"/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/copy.py\", line 180, in deepcopy\r\n    y = _reconstruct(x, memo, *rv)\r\n  File \"/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/copy.py\", line 281, in _reconstruct\r\n    if hasattr(y, '__setstate__'):\r\n  File \"/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/path.py\", line 77, in __getattr__\r\n    r = getattr(self._path, k)\r\n  File \"/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/path.py\", line 77, in __getattr__\r\n    r = getattr(self._path, k)\r\n  File \"/path/vd_plugins/radare2/r2/lib/python3.7/site-packages/visidata-2._4dev-py3.7.egg/visidata/path.py\", line 77, in __getattr__\r\n    r = getattr(self._path, k)\r\n  [Previous line repeated 492 more times]\r\nRecursionError: maximum recursion depth exceeded\r\n```\r\n### Replicate:\r\n\r\n1. Use v2.-4dev branch\r\n2. Open a DirSheet with `vd .`\r\n3. Do a deep copy of the sheet with `dup-rows-deep`\r\n\r\n### Result:\r\nA sheet with no rows, columns as expected for a DirSheet, and the error message above.\r\n\r\n### Expected:\r\nA deep copy of the original DirSheet with all rows (including those selected)\r\n\r\n### cmdlog:\r\n```\r\nsheet\tcol\trow\tlongname\tinput\tkeystrokes\tcomment\r\n\tSqliteSheet\theader\tset-option\t0\r\n\tUsvSheet\tdelimiter\tset-option\t\u241e\r\n\tUsvSheet\trow_delimiter\tset-option\t\u241f\r\n\toverride\tdisp_date_fmt\tset-option\t%Y-%m-%d %H:%M:%S\r\n\t\t\topen-file\t.\to\r\n_files\t\t\tdup-rows-deep\t\tgz\"\topen duplicate sheet with deepcopy of all rows\r\n```\r\n\r\n\r\nAlso, am I correct in understanding that if I make a deep copy, modifications I make to that copy should propagate to the original sheet? And this should include selecting/deselecting rows?\n", "before_files": [{"content": "import os\nimport os.path\nimport sys\nimport pathlib\nfrom urllib.parse import urlparse, urlunparse\n\nfrom visidata import *\n\noption('encoding', 'utf-8', 'encoding passed to codecs.open', replay=True)\noption('encoding_errors', 'surrogateescape', 'encoding_errors passed to codecs.open', replay=True)\n\n@functools.lru_cache()\ndef vstat(path, force=False):\n    try:\n        return os.stat(path)\n    except Exception as e:\n        return None\n\ndef filesize(path):\n    if hasattr(path, 'filesize') and path.filesize is not None:\n        return path.filesize\n    if path.fp or path.is_url():\n        return 0\n    st = path.stat() # vstat(path)\n    return st and st.st_size\n\ndef modtime(path):\n    st = path.stat()\n    return st and st.st_mtime\n\n\nclass Path(os.PathLike):\n    'File and path-handling class, modeled on `pathlib.Path`.'\n    def __init__(self, given, fp=None, lines=None, filesize=None):\n        # Resolve pathname shell variables and ~userdir\n        self.given = os.path.expandvars(os.path.expanduser(given))\n        self.fp = fp\n        self.lines = lines or []  # shared among all RepeatFile instances\n        self.filesize = filesize\n        self.rfile = None\n\n    @functools.lru_cache()\n    def stat(self, force=False):\n        return self._path.stat()\n\n    @property\n    def given(self):\n        return self._given\n\n    @given.setter\n    def given(self, given):\n        self._given = given\n        if isinstance(given, os.PathLike):\n            self._path = given\n        else:\n            self._path = pathlib.Path(given)\n\n        self.ext = self.suffix[1:]\n        if self.suffix:\n            self.name = self._path.name[:-len(self.suffix)]\n        else:\n            self.name = self._path.name\n\n        # check if file is compressed\n        if self.suffix in ['.gz', '.bz2', '.xz']:\n            self.compression = self.ext\n            uncompressedpath = Path(self.given[:-len(self.suffix)])\n            self.name = uncompressedpath.name\n            self.ext = uncompressedpath.ext\n        else:\n            self.compression = None\n\n    def __getattr__(self, k):\n        if hasattr(self.__dict__, k):\n            r = getattr(self.__dict__, k)\n        else:\n            r = getattr(self._path, k)\n        if isinstance(r, pathlib.Path):\n            return Path(r)\n        return r\n\n    def __fspath__(self):\n        return self._path.__fspath__()\n\n    def __lt__(self, a):\n        return self._path.__lt__(a)\n\n    def __truediv__(self, a):\n        return Path(self._path.__truediv__(a))\n\n    def open_text(self, mode='rt'):\n        # rfile makes a single-access fp reusable\n\n        if self.rfile:\n            return self.rfile\n\n        if self.fp:\n            self.rfile = RepeatFile(fp=self.fp)\n            return self.rfile\n\n        if 't' not in mode:\n            mode += 't'\n\n        if self.given == '-':\n            if 'r' in mode:\n                return vd._stdin\n            elif 'w' in mode or 'a' in mode:\n                # convert 'a' to 'w' for stdout: https://bugs.python.org/issue27805\n                return open(os.dup(vd._stdout.fileno()), 'wt')\n            else:\n                error('invalid mode \"%s\" for Path.open_text()' % mode)\n                return sys.stderr\n\n        return self.open(mode=mode, encoding=options.encoding, errors=options.encoding_errors)\n\n    def read_text(self, *args):\n        if self.lines:\n            return RepeatFile(iter_lines=self.lines).read()\n        elif self.fp:\n            return self.fp.read()\n        else:\n            return self._path.read_text(*args)\n\n    def open(self, *args, **kwargs):\n        fn = self\n        if self.compression == 'gz':\n            import gzip\n            return gzip.open(fn, *args, **kwargs)\n        elif self.compression == 'bz2':\n            import bz2\n            return bz2.open(fn, *args, **kwargs)\n        elif self.compression == 'xz':\n            import lzma\n            return lzma.open(fn, *args, **kwargs)\n        else:\n            return self._path.open(*args, **kwargs)\n\n    def __iter__(self):\n        with Progress(total=filesize(self)) as prog:\n            for i, line in enumerate(self.open_text()):\n                prog.addProgress(len(line))\n                yield line[:-1]\n\n    def open_bytes(self, mode='rb'):\n        if 'b' not in mode:\n            mode += 'b'\n        return self.open(mode=mode)\n\n    def read_bytes(self):\n        with self.open(mode='rb') as fp:\n            return fp.read()\n\n    def is_url(self):\n        return '://' in self.given\n\n    def __str__(self):\n        if self.is_url():\n            return self.given\n        return str(self._path)\n\n    @functools.lru_cache()\n    def stat(self, force=False):\n        try:\n            if not self.is_url():\n                return self._path.stat()\n        except Exception as e:\n            return None\n\n    def exists(self):\n        if self.fp or self.is_url():\n            return True\n        return self._path.exists()\n\n    @property\n    def scheme(self):\n        if self.is_url():\n            return urlparse(self.given).scheme\n\n    def with_name(self, name):\n        if self.is_url():\n            urlparts = list(urlparse(self.given))\n            urlparts[2] = '/'.join(Path(urlparts[2])._parts[1:-1] + [name])\n            return Path(urlunparse(urlparts))\n        else:\n            return Path(self._from_parsed_parts(self._drv, self._root, self._parts[:-1] + [name]))\n\n\nclass RepeatFile:\n    def __init__(self, *, fp=None, iter_lines=None):\n        'Provide either fp or iter_lines, and lines will be filled from it.'\n        self.fp = fp\n        self.iter_lines = iter_lines\n        self.lines = []\n        self.iter = RepeatFileIter(self)\n\n    def __enter__(self):\n        self.iter = RepeatFileIter(self)\n        return self\n\n    def __exit__(self, a,b,c):\n        pass\n\n    def read(self, n=None):\n        r = ''\n        if n is None:\n            n = 10**12  # some too huge number\n        while len(r) < n:\n            try:\n                s = next(self.iter)\n                r += s + '\\n'\n                n += len(r)\n            except StopIteration:\n                break  # end of file\n        return r\n\n    def seek(self, n):\n        assert n == 0, 'RepeatFile can only seek to beginning'\n        self.iter = RepeatFileIter(self)\n\n    def __iter__(self):\n        return RepeatFileIter(self)\n\n    def __next__(self):\n        return next(self.iter)\n\n    def exists(self):\n        return True\n\n\nclass RepeatFileIter:\n    def __init__(self, rf):\n        self.rf = rf\n        self.nextIndex = 0\n\n    def __iter__(self):\n        return RepeatFileIter(self.rf)\n\n    def __next__(self):\n        if self.nextIndex < len(self.rf.lines):\n            r = self.rf.lines[self.nextIndex]\n        elif self.rf.iter_lines:\n            try:\n                r = next(self.rf.iter_lines)\n                self.rf.lines.append(r)\n            except StopIteration:\n                self.rf.iter_lines = None\n                raise\n        elif self.rf.fp:\n            try:\n                r = next(self.rf.fp)\n                self.rf.lines.append(r)\n            except StopIteration:\n                self.rf.fp = None\n                raise\n        else:\n            raise StopIteration()\n\n\n        self.nextIndex += 1\n        return r\n", "path": "visidata/path.py"}], "after_files": [{"content": "import os\nimport os.path\nimport sys\nimport pathlib\nfrom urllib.parse import urlparse, urlunparse\n\nfrom visidata import *\n\noption('encoding', 'utf-8', 'encoding passed to codecs.open', replay=True)\noption('encoding_errors', 'surrogateescape', 'encoding_errors passed to codecs.open', replay=True)\n\n@functools.lru_cache()\ndef vstat(path, force=False):\n    try:\n        return os.stat(path)\n    except Exception as e:\n        return None\n\ndef filesize(path):\n    if hasattr(path, 'filesize') and path.filesize is not None:\n        return path.filesize\n    if path.fp or path.is_url():\n        return 0\n    st = path.stat() # vstat(path)\n    return st and st.st_size\n\ndef modtime(path):\n    st = path.stat()\n    return st and st.st_mtime\n\n\nclass Path(os.PathLike):\n    'File and path-handling class, modeled on `pathlib.Path`.'\n    def __init__(self, given, fp=None, lines=None, filesize=None):\n        # Resolve pathname shell variables and ~userdir\n        self.given = os.path.expandvars(os.path.expanduser(given))\n        self.fp = fp\n        self.lines = lines or []  # shared among all RepeatFile instances\n        self.filesize = filesize\n        self.rfile = None\n\n    @functools.lru_cache()\n    def stat(self, force=False):\n        return self._path.stat()\n\n    @property\n    def given(self):\n        return self._given\n\n    @given.setter\n    def given(self, given):\n        self._given = given\n        if isinstance(given, os.PathLike):\n            self._path = given\n        else:\n            self._path = pathlib.Path(given)\n\n        self.ext = self.suffix[1:]\n        if self.suffix:\n            self.name = self._path.name[:-len(self.suffix)]\n        else:\n            self.name = self._path.name\n\n        # check if file is compressed\n        if self.suffix in ['.gz', '.bz2', '.xz']:\n            self.compression = self.ext\n            uncompressedpath = Path(self.given[:-len(self.suffix)])\n            self.name = uncompressedpath.name\n            self.ext = uncompressedpath.ext\n        else:\n            self.compression = None\n\n    def __getattr__(self, k):\n        if hasattr(self.__dict__, k):\n            r = getattr(self.__dict__, k)\n        else:\n            if self.__dict__.get('_path', None) is not None:\n                r = getattr(self._path, k)\n            else:\n                raise AttributeError(k)\n        if isinstance(r, pathlib.Path):\n            return Path(r)\n        return r\n\n    def __fspath__(self):\n        return self._path.__fspath__()\n\n    def __lt__(self, a):\n        return self._path.__lt__(a)\n\n    def __truediv__(self, a):\n        return Path(self._path.__truediv__(a))\n\n    def open_text(self, mode='rt'):\n        # rfile makes a single-access fp reusable\n\n        if self.rfile:\n            return self.rfile\n\n        if self.fp:\n            self.rfile = RepeatFile(fp=self.fp)\n            return self.rfile\n\n        if 't' not in mode:\n            mode += 't'\n\n        if self.given == '-':\n            if 'r' in mode:\n                return vd._stdin\n            elif 'w' in mode or 'a' in mode:\n                # convert 'a' to 'w' for stdout: https://bugs.python.org/issue27805\n                return open(os.dup(vd._stdout.fileno()), 'wt')\n            else:\n                error('invalid mode \"%s\" for Path.open_text()' % mode)\n                return sys.stderr\n\n        return self.open(mode=mode, encoding=options.encoding, errors=options.encoding_errors)\n\n    def read_text(self, *args):\n        if self.lines:\n            return RepeatFile(iter_lines=self.lines).read()\n        elif self.fp:\n            return self.fp.read()\n        else:\n            return self._path.read_text(*args)\n\n    def open(self, *args, **kwargs):\n        fn = self\n        if self.compression == 'gz':\n            import gzip\n            return gzip.open(fn, *args, **kwargs)\n        elif self.compression == 'bz2':\n            import bz2\n            return bz2.open(fn, *args, **kwargs)\n        elif self.compression == 'xz':\n            import lzma\n            return lzma.open(fn, *args, **kwargs)\n        else:\n            return self._path.open(*args, **kwargs)\n\n    def __iter__(self):\n        with Progress(total=filesize(self)) as prog:\n            for i, line in enumerate(self.open_text()):\n                prog.addProgress(len(line))\n                yield line[:-1]\n\n    def open_bytes(self, mode='rb'):\n        if 'b' not in mode:\n            mode += 'b'\n        return self.open(mode=mode)\n\n    def read_bytes(self):\n        with self.open(mode='rb') as fp:\n            return fp.read()\n\n    def is_url(self):\n        return '://' in self.given\n\n    def __str__(self):\n        if self.is_url():\n            return self.given\n        return str(self._path)\n\n    @functools.lru_cache()\n    def stat(self, force=False):\n        try:\n            if not self.is_url():\n                return self._path.stat()\n        except Exception as e:\n            return None\n\n    def exists(self):\n        if self.fp or self.is_url():\n            return True\n        return self._path.exists()\n\n    @property\n    def scheme(self):\n        if self.is_url():\n            return urlparse(self.given).scheme\n\n    def with_name(self, name):\n        if self.is_url():\n            urlparts = list(urlparse(self.given))\n            urlparts[2] = '/'.join(Path(urlparts[2])._parts[1:-1] + [name])\n            return Path(urlunparse(urlparts))\n        else:\n            return Path(self._from_parsed_parts(self._drv, self._root, self._parts[:-1] + [name]))\n\n\nclass RepeatFile:\n    def __init__(self, *, fp=None, iter_lines=None):\n        'Provide either fp or iter_lines, and lines will be filled from it.'\n        self.fp = fp\n        self.iter_lines = iter_lines\n        self.lines = []\n        self.iter = RepeatFileIter(self)\n\n    def __enter__(self):\n        self.iter = RepeatFileIter(self)\n        return self\n\n    def __exit__(self, a,b,c):\n        pass\n\n    def read(self, n=None):\n        r = ''\n        if n is None:\n            n = 10**12  # some too huge number\n        while len(r) < n:\n            try:\n                s = next(self.iter)\n                r += s + '\\n'\n                n += len(r)\n            except StopIteration:\n                break  # end of file\n        return r\n\n    def seek(self, n):\n        assert n == 0, 'RepeatFile can only seek to beginning'\n        self.iter = RepeatFileIter(self)\n\n    def __iter__(self):\n        return RepeatFileIter(self)\n\n    def __next__(self):\n        return next(self.iter)\n\n    def exists(self):\n        return True\n\n\nclass RepeatFileIter:\n    def __init__(self, rf):\n        self.rf = rf\n        self.nextIndex = 0\n\n    def __iter__(self):\n        return RepeatFileIter(self.rf)\n\n    def __next__(self):\n        if self.nextIndex < len(self.rf.lines):\n            r = self.rf.lines[self.nextIndex]\n        elif self.rf.iter_lines:\n            try:\n                r = next(self.rf.iter_lines)\n                self.rf.lines.append(r)\n            except StopIteration:\n                self.rf.iter_lines = None\n                raise\n        elif self.rf.fp:\n            try:\n                r = next(self.rf.fp)\n                self.rf.lines.append(r)\n            except StopIteration:\n                self.rf.fp = None\n                raise\n        else:\n            raise StopIteration()\n\n\n        self.nextIndex += 1\n        return r\n", "path": "visidata/path.py"}]}