siliconcompiler 0.33.1__py3-none-any.whl → 0.34.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. siliconcompiler/__init__.py +2 -0
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/sc_issue.py +5 -3
  4. siliconcompiler/apps/sc_remote.py +0 -17
  5. siliconcompiler/apps/utils/replay.py +5 -5
  6. siliconcompiler/checklist.py +1 -1
  7. siliconcompiler/core.py +39 -48
  8. siliconcompiler/data/templates/replay/replay.sh.j2 +18 -1
  9. siliconcompiler/dependencyschema.py +392 -0
  10. siliconcompiler/design.py +664 -0
  11. siliconcompiler/flowgraph.py +32 -1
  12. siliconcompiler/metric.py +19 -0
  13. siliconcompiler/package/__init__.py +383 -223
  14. siliconcompiler/package/git.py +75 -77
  15. siliconcompiler/package/github.py +70 -97
  16. siliconcompiler/package/https.py +77 -93
  17. siliconcompiler/packageschema.py +260 -0
  18. siliconcompiler/pdk.py +2 -2
  19. siliconcompiler/record.py +57 -5
  20. siliconcompiler/remote/client.py +61 -13
  21. siliconcompiler/remote/server.py +109 -64
  22. siliconcompiler/report/dashboard/cli/board.py +1 -2
  23. siliconcompiler/scheduler/__init__.py +3 -1375
  24. siliconcompiler/scheduler/docker.py +268 -0
  25. siliconcompiler/scheduler/run_node.py +20 -19
  26. siliconcompiler/scheduler/scheduler.py +308 -0
  27. siliconcompiler/scheduler/schedulernode.py +934 -0
  28. siliconcompiler/scheduler/slurm.py +147 -163
  29. siliconcompiler/scheduler/taskscheduler.py +39 -52
  30. siliconcompiler/schema/__init__.py +3 -3
  31. siliconcompiler/schema/baseschema.py +256 -11
  32. siliconcompiler/schema/editableschema.py +4 -0
  33. siliconcompiler/schema/journal.py +210 -0
  34. siliconcompiler/schema/namedschema.py +31 -2
  35. siliconcompiler/schema/parameter.py +14 -1
  36. siliconcompiler/schema/parametervalue.py +1 -34
  37. siliconcompiler/schema/schema_cfg.py +211 -350
  38. siliconcompiler/tool.py +139 -37
  39. siliconcompiler/tools/_common/__init__.py +14 -11
  40. siliconcompiler/tools/builtin/concatenate.py +2 -2
  41. siliconcompiler/tools/builtin/verify.py +1 -2
  42. siliconcompiler/tools/openroad/scripts/common/procs.tcl +27 -25
  43. siliconcompiler/tools/slang/__init__.py +3 -2
  44. siliconcompiler/tools/vpr/route.py +69 -0
  45. siliconcompiler/tools/yosys/sc_synth_asic.tcl +0 -4
  46. siliconcompiler/toolscripts/_tools.json +13 -8
  47. siliconcompiler/toolscripts/ubuntu22/install-klayout.sh +4 -0
  48. siliconcompiler/toolscripts/ubuntu24/install-klayout.sh +4 -0
  49. siliconcompiler/utils/__init__.py +2 -23
  50. siliconcompiler/utils/flowgraph.py +5 -5
  51. siliconcompiler/utils/logging.py +2 -1
  52. {siliconcompiler-0.33.1.dist-info → siliconcompiler-0.34.0.dist-info}/METADATA +8 -6
  53. {siliconcompiler-0.33.1.dist-info → siliconcompiler-0.34.0.dist-info}/RECORD +57 -52
  54. {siliconcompiler-0.33.1.dist-info → siliconcompiler-0.34.0.dist-info}/WHEEL +1 -1
  55. siliconcompiler/scheduler/docker_runner.py +0 -254
  56. siliconcompiler/schema/journalingschema.py +0 -238
  57. {siliconcompiler-0.33.1.dist-info → siliconcompiler-0.34.0.dist-info}/entry_points.txt +0 -0
  58. {siliconcompiler-0.33.1.dist-info → siliconcompiler-0.34.0.dist-info}/licenses/LICENSE +0 -0
  59. {siliconcompiler-0.33.1.dist-info → siliconcompiler-0.34.0.dist-info}/top_level.txt +0 -0
@@ -3,82 +3,80 @@ import shutil
3
3
  import os.path
4
4
 
5
5
  from git import Repo, GitCommandError
6
- from fasteners import InterProcessLock
7
-
8
- from siliconcompiler.package import get_download_cache_path
9
- from siliconcompiler.package import aquire_data_lock, release_data_lock
10
-
11
-
12
- def get_resolver(url):
13
- if url.scheme in ("git", "git+https", "git+ssh", "ssh"):
14
- return git_resolver
15
- return None
16
-
17
-
18
- def git_resolver(chip, package, path, ref, url, fetch):
19
- data_path, data_path_lock = get_download_cache_path(chip, package, ref)
20
-
21
- if not fetch:
22
- return data_path, False
23
-
24
- # Acquire lock
25
- data_lock = InterProcessLock(data_path_lock)
26
- aquire_data_lock(data_path, data_lock)
6
+ from siliconcompiler.package import RemoteResolver
7
+
8
+
9
+ def get_resolver():
10
+ return {
11
+ "git": GitResolver,
12
+ "git+https": GitResolver,
13
+ "git+ssh": GitResolver,
14
+ "ssh": GitResolver
15
+ }
16
+
17
+
18
+ class GitResolver(RemoteResolver):
19
+ def __init__(self, name, root, source, reference=None):
20
+ super().__init__(name, root, source, reference)
21
+
22
+ def check_cache(self):
23
+ if os.path.exists(self.cache_path):
24
+ try:
25
+ repo = Repo(self.cache_path)
26
+ if repo.untracked_files or repo.index.diff("HEAD"):
27
+ self.logger.warning('The repo of the cached data is dirty.')
28
+ return True
29
+ except GitCommandError:
30
+ self.logger.warning('Deleting corrupted cache data.')
31
+ shutil.rmtree(self.cache_path)
32
+ return False
33
+ return False
34
+
35
+ def __get_token_env(self):
36
+ token_name = self.name.upper()
37
+ for tok in ('#', '$', '&', '-', '=', '!', '/'):
38
+ token_name = token_name.replace(tok, '')
39
+
40
+ search_env = (
41
+ f'GITHUB_{token_name}_TOKEN',
42
+ 'GITHUB_TOKEN',
43
+ 'GIT_TOKEN'
44
+ )
45
+
46
+ token = None
47
+ for env in search_env:
48
+ token = os.environ.get(env, None)
49
+
50
+ if token:
51
+ return token
52
+ return None
53
+
54
+ @property
55
+ def git_path(self):
56
+ if self.urlscheme == "git+ssh":
57
+ return f"ssh://{self.urlpath}{self.urlparse.path}"
58
+ if self.urlscheme == "ssh":
59
+ return self.source
60
+ url = self.urlparse
61
+ if not url.username and self.__get_token_env():
62
+ url = url._replace(netloc=f'{self.__get_token_env()}@{url.hostname}')
63
+ url = url._replace(scheme='https')
64
+ return url.geturl()
27
65
 
28
- if os.path.exists(data_path):
66
+ def resolve_remote(self):
29
67
  try:
30
- repo = Repo(data_path)
31
- if repo.untracked_files or repo.index.diff("HEAD"):
32
- chip.logger.warning('The repo of the cached data is dirty.')
33
- release_data_lock(data_lock)
34
- return data_path, False
35
- except GitCommandError:
36
- chip.logger.warning('Deleting corrupted cache data.')
37
- shutil.rmtree(data_path)
38
-
39
- clone_synchronized(chip, package, path, ref, url, data_path)
40
-
41
- release_data_lock(data_lock)
42
-
43
- return data_path, True
44
-
45
-
46
- def clone_synchronized(chip, package, path, ref, url, data_path):
47
- try:
48
- clone_from_git(chip, package, path, ref, url, data_path)
49
- except GitCommandError as e:
50
- if 'Permission denied' in repr(e):
51
- if url.scheme in ['ssh', 'git+ssh']:
52
- chip.logger.error('Failed to authenticate. Please setup your git ssh.')
53
- elif url.scheme in ['git', 'git+https']:
54
- chip.logger.error('Failed to authenticate. Please use a token or ssh.')
55
- else:
56
- chip.logger.error(str(e))
57
-
58
-
59
- def clone_from_git(chip, package, path, ref, url, data_path):
60
- if url.scheme in ['git', 'git+https'] and url.username:
61
- chip.logger.warning('Your token is in the data source path and will be stored in the '
62
- 'schema. If you do not want this set the env variable GIT_TOKEN '
63
- 'or use ssh for authentication.')
64
- if url.scheme in ['git+ssh']:
65
- chip.logger.info(f'Cloning {package} data from {url.netloc}:{url.path[1:]}')
66
- # Git requires the format git@github.com:org/repo instead of git@github.com/org/repo
67
- repo = Repo.clone_from(f'{url.netloc}/{url.path[1:]}',
68
- data_path,
69
- recurse_submodules=True)
70
- elif url.scheme in ['ssh']:
71
- chip.logger.info(f'Cloning {package} data from {path}')
72
- repo = Repo.clone_from(path,
73
- data_path,
74
- recurse_submodules=True)
75
- else:
76
- if os.environ.get('GIT_TOKEN') and not url.username:
77
- url = url._replace(netloc=f'{os.environ.get("GIT_TOKEN")}@{url.hostname}')
78
- url = url._replace(scheme='https')
79
- chip.logger.info(f'Cloning {package} data from {url.geturl()}')
80
- repo = Repo.clone_from(url.geturl(), data_path, recurse_submodules=True)
81
- chip.logger.info(f'Checking out {ref}')
82
- repo.git.checkout(ref)
83
- for submodule in repo.submodules:
84
- submodule.update(init=True)
68
+ path = self.git_path
69
+ self.logger.info(f'Cloning {self.name} data from {path}')
70
+ repo = Repo.clone_from(path, self.cache_path, recurse_submodules=True)
71
+ self.logger.info(f'Checking out {self.reference}')
72
+ repo.git.checkout(self.reference)
73
+ for submodule in repo.submodules:
74
+ submodule.update(recursive=True, init=True, force=True)
75
+ except GitCommandError as e:
76
+ if 'Permission denied' in repr(e):
77
+ if self.urlscheme in ('ssh', 'git+ssh'):
78
+ raise RuntimeError('Failed to authenticate. Please setup your git ssh.')
79
+ elif self.urlscheme in ('git', 'git+https'):
80
+ raise RuntimeError('Failed to authenticate. Please use a token or ssh.')
81
+ else:
82
+ raise e
@@ -1,124 +1,97 @@
1
1
  import os
2
- from fasteners import InterProcessLock
2
+
3
3
  from github import Github, Auth
4
4
  from github.GithubException import UnknownObjectException
5
- from urllib.parse import urlparse
6
- from siliconcompiler.package import get_download_cache_path
7
- from siliconcompiler.package import aquire_data_lock, release_data_lock
8
- from siliconcompiler.package.https import _http_resolver
9
-
10
-
11
- def get_resolver(url):
12
- if url.scheme in ("github",):
13
- return github_any_resolver
14
- if url.scheme in ("github+private",):
15
- return github_private_resolver
16
- return None
17
-
18
-
19
- def github_any_resolver(chip, package, path, ref, url, fetch):
20
- data_path, data_path_lock = get_download_cache_path(chip, package, ref)
21
-
22
- if not fetch:
23
- return data_path, False
24
-
25
- # Acquire lock
26
- data_lock = InterProcessLock(data_path_lock)
27
- aquire_data_lock(data_path, data_lock)
28
-
29
- if os.path.exists(data_path):
30
- release_data_lock(data_lock)
31
- return data_path, False
32
-
33
- try:
34
- return _github_resolver(chip, package, path, ref, url, data_lock)
35
- except UnknownObjectException:
36
- return github_private_resolver(chip, package, path, ref, url, fetch, data_lock=data_lock)
37
-
38
-
39
- def github_private_resolver(chip, package, path, ref, url, fetch, data_lock=None):
40
- data_path, data_path_lock = get_download_cache_path(chip, package, ref)
41
-
42
- if not fetch:
43
- return data_path, False
44
-
45
- if not data_lock:
46
- # Acquire lock
47
- data_lock = InterProcessLock(data_path_lock)
48
- aquire_data_lock(data_path, data_lock)
49
5
 
50
- if os.path.exists(data_path):
51
- release_data_lock(data_lock)
52
- return data_path, False
6
+ from siliconcompiler.package.https import HTTPResolver
53
7
 
54
- gh = Github(auth=Auth.Token(__get_github_auth_token(package)))
55
8
 
56
- return _github_resolver(chip, package, path, ref, url, data_lock, gh=gh)
9
+ def get_resolver():
10
+ return {
11
+ "github": GithubResolver,
12
+ "github+private": GithubResolver
13
+ }
57
14
 
58
15
 
59
- def _github_resolver(chip, package, path, ref, url, data_lock, gh=None):
60
- if not gh:
61
- gh = Github()
16
+ class GithubResolver(HTTPResolver):
17
+ def __init__(self, name, root, source, reference=None):
18
+ super().__init__(name, root, source, reference)
62
19
 
63
- url_parts = (url.netloc, *url.path.split("/")[1:])
20
+ if len(self.gh_path) != 4:
21
+ raise ValueError(
22
+ f"{self.source} is not in the proper form: "
23
+ "<owner>/<repository>/<version>/<artifact>")
64
24
 
65
- if len(url_parts) != 4:
66
- raise ValueError(
67
- f"{path} is not in the proper form: <owner>/<repository>/<version>/<artifact>")
25
+ @property
26
+ def gh_path(self):
27
+ return self.urlpath, *self.urlparse.path.split("/")[1:]
68
28
 
69
- repository = "/".join(url_parts[0:2])
70
- release = url_parts[2]
71
- artifact = url_parts[3]
29
+ @property
30
+ def download_url(self):
31
+ url_parts = self.gh_path
72
32
 
73
- release_url = __get_release_url(gh, repository, release, artifact)
33
+ repository = "/".join(url_parts[0:2])
34
+ release = url_parts[2]
35
+ artifact = url_parts[3]
74
36
 
75
- return _http_resolver(chip, package, release_url, ref, urlparse(release_url), data_lock)
37
+ if self.urlscheme == "github+private":
38
+ return self.__get_release_url(repository, release, artifact, True)
76
39
 
40
+ try:
41
+ return self.__get_release_url(repository, release, artifact, False)
42
+ except UnknownObjectException:
43
+ return self.__get_release_url(repository, release, artifact, True)
77
44
 
78
- def __get_release_url(gh, repository, release, artifact):
79
- if artifact == f"{release}.zip":
80
- return f"https://github.com/{repository}/archive/refs/tags/{release}.zip"
81
- if artifact == f"{release}.tar.gz":
82
- return f"https://github.com/{repository}/archive/refs/tags/{release}.tar.gz"
45
+ def __get_release_url(self, repository, release, artifact, private: bool):
46
+ if artifact == f"{release}.zip":
47
+ return f"https://github.com/{repository}/archive/refs/tags/{release}.zip"
48
+ if artifact == f"{release}.tar.gz":
49
+ return f"https://github.com/{repository}/archive/refs/tags/{release}.tar.gz"
83
50
 
84
- repo = gh.get_repo(repository)
51
+ repo = self.__gh(private).get_repo(repository)
85
52
 
86
- if not release:
87
- release = repo.get_latest_release().tag_name
53
+ if not release:
54
+ release = repo.get_latest_release().tag_name
88
55
 
89
- url = None
90
- for repo_release in repo.get_releases():
91
- if repo_release.tag_name == release:
92
- for asset in repo_release.assets:
93
- if asset.name == artifact:
94
- url = asset.url
56
+ url = None
57
+ for repo_release in repo.get_releases():
58
+ if repo_release.tag_name == release:
59
+ for asset in repo_release.assets:
60
+ if asset.name == artifact:
61
+ url = asset.url
95
62
 
96
- if not url:
97
- raise ValueError(f'Unable to find release asset: {repository}/{release}/{artifact}')
63
+ if not url:
64
+ raise ValueError(f'Unable to find release asset: {repository}/{release}/{artifact}')
98
65
 
99
- return url
66
+ return url
100
67
 
68
+ def __get_gh_auth(self):
69
+ token_name = self.name.upper()
70
+ for tok in ('#', '$', '&', '-', '=', '!', '/'):
71
+ token_name = token_name.replace(tok, '')
101
72
 
102
- def __get_github_auth_token(package_name):
103
- token_name = package_name.upper()
104
- for tok in ('#', '$', '&', '-', '=', '!', '/'):
105
- token_name = token_name.replace(tok, '')
73
+ search_env = (
74
+ f'GITHUB_{token_name}_TOKEN',
75
+ 'GITHUB_TOKEN',
76
+ 'GIT_TOKEN'
77
+ )
106
78
 
107
- search_env = (
108
- f'GITHUB_{token_name}_TOKEN',
109
- 'GITHUB_TOKEN',
110
- 'GIT_TOKEN'
111
- )
79
+ token = None
80
+ for env in search_env:
81
+ token = os.environ.get(env, None)
112
82
 
113
- token = None
114
- for env in search_env:
115
- token = os.environ.get(env, None)
83
+ if token:
84
+ break
116
85
 
117
- if token:
118
- break
86
+ if not token:
87
+ raise ValueError('Unable to determine authorization token for GitHub, '
88
+ 'please set one of the following environmental variables: '
89
+ f'{", ".join(search_env)}')
119
90
 
120
- if not token:
121
- raise ValueError('Unable to determine authorization token for GitHub, '
122
- f'please set one of the following environmental variables: {search_env}')
91
+ return token
123
92
 
124
- return token
93
+ def __gh(self, private: bool) -> Github:
94
+ if private:
95
+ return Github(auth=Auth.Token(self.__get_gh_auth()))
96
+ else:
97
+ return Github()
@@ -5,99 +5,83 @@ import zipfile
5
5
 
6
6
  import os.path
7
7
 
8
- from fasteners import InterProcessLock
9
8
  from io import BytesIO
10
9
  from urllib.parse import urlparse
11
10
 
12
- from siliconcompiler import SiliconCompilerError
13
- from siliconcompiler.package import get_download_cache_path
14
- from siliconcompiler.package import aquire_data_lock, release_data_lock
15
-
16
-
17
- def get_resolver(url):
18
- if url.scheme in ("http", "https"):
19
- return http_resolver
20
-
21
- return None
22
-
23
-
24
- def http_resolver(chip, package, path, ref, url, fetch):
25
- data_path, data_path_lock = get_download_cache_path(chip, package, ref)
26
-
27
- if not fetch:
28
- return data_path, False
29
-
30
- # Acquire lock
31
- data_lock = InterProcessLock(data_path_lock)
32
- aquire_data_lock(data_path, data_lock)
33
-
34
- if os.path.exists(data_path):
35
- release_data_lock(data_lock)
36
- return data_path, False
37
-
38
- return _http_resolver(chip, package, path, ref, url, data_lock)
39
-
40
-
41
- def _http_resolver(chip, package, path, ref, url, data_lock):
42
- data_path, _ = get_download_cache_path(chip, package, ref)
43
-
44
- extract_from_url(chip, package, path, ref, url, data_path)
45
-
46
- release_data_lock(data_lock)
47
-
48
- return data_path, True
49
-
50
-
51
- def extract_from_url(chip, package, path, ref, url, data_path):
52
- data_url = path
53
- headers = {}
54
- if os.environ.get('GIT_TOKEN') or url.username:
55
- headers['Authorization'] = f'token {os.environ.get("GIT_TOKEN") or url.username}'
56
- if "github" in data_url:
57
- headers['Accept'] = 'application/octet-stream'
58
- data_url = path
59
- if data_url.endswith('/'):
60
- data_url = f"{data_url}{ref}.tar.gz"
61
- chip.logger.info(f'Downloading {package} data from {data_url}')
62
- response = requests.get(data_url, stream=True, headers=headers)
63
- if not response.ok:
64
- raise SiliconCompilerError(f'Failed to download {package} data source.', chip=chip)
65
-
66
- fileobj = BytesIO(response.content)
67
- try:
68
- with tarfile.open(fileobj=fileobj, mode='r|gz') as tar_ref:
69
- tar_ref.extractall(path=data_path)
70
- except tarfile.ReadError:
71
- fileobj.seek(0)
72
- # Try as zip
73
- with zipfile.ZipFile(fileobj) as zip_ref:
74
- zip_ref.extractall(path=data_path)
75
-
76
- if 'github' in url.netloc and len(os.listdir(data_path)) == 1:
77
- # Github inserts one folder at the highest level of the tar file
78
- # this compensates for this behavior
79
- gh_url = urlparse(data_url)
80
-
81
- repo = gh_url.path.split('/')[2]
82
-
83
- gh_ref = gh_url.path.split('/')[-1]
84
- if repo.endswith('.git'):
85
- gh_ref = ref
86
- elif gh_ref.endswith('.tar.gz'):
87
- gh_ref = gh_ref[0:-7]
88
- elif gh_ref.endswith('.tgz'):
89
- gh_ref = gh_ref[0:-4]
90
- else:
91
- gh_ref = gh_ref.split('.')[0]
92
-
93
- if gh_ref.startswith('v'):
94
- gh_ref = gh_ref[1:]
95
-
96
- github_folder = f"{repo}-{gh_ref}"
97
-
98
- if github_folder in os.listdir(data_path):
99
- # This moves all files one level up
100
- git_path = os.path.join(data_path, github_folder)
101
- for data_file in os.listdir(git_path):
102
- shutil.move(os.path.join(git_path, data_file), data_path)
103
- os.removedirs(git_path)
11
+ from siliconcompiler.package import RemoteResolver
12
+
13
+
14
+ def get_resolver():
15
+ return {
16
+ "http": HTTPResolver,
17
+ "https": HTTPResolver
18
+ }
19
+
20
+
21
+ class HTTPResolver(RemoteResolver):
22
+ def check_cache(self):
23
+ return os.path.exists(self.cache_path)
24
+
25
+ @property
26
+ def download_url(self):
27
+ data_url = self.source
28
+ if data_url.endswith('/'):
29
+ data_url = f"{data_url}{self.reference}.tar.gz"
30
+ return data_url
31
+
32
+ def resolve_remote(self):
33
+ data_url = self.download_url
34
+
35
+ headers = {}
36
+ auth_token = os.environ.get('GIT_TOKEN', self.urlparse.username)
37
+ if auth_token:
38
+ headers['Authorization'] = f'token {auth_token}'
39
+ if "github" in data_url:
40
+ headers['Accept'] = 'application/octet-stream'
41
+
42
+ self.logger.info(f'Downloading {self.name} data from {data_url}')
43
+
44
+ response = requests.get(data_url, stream=True, headers=headers)
45
+ if not response.ok:
46
+ raise FileNotFoundError(f'Failed to download {self.name} data source.')
47
+
48
+ os.makedirs(self.cache_path, exist_ok=True)
49
+
50
+ fileobj = BytesIO(response.content)
51
+ try:
52
+ with tarfile.open(fileobj=fileobj, mode='r|gz') as tar_ref:
53
+ tar_ref.extractall(path=self.cache_path)
54
+ except tarfile.ReadError:
55
+ fileobj.seek(0)
56
+ # Try as zip
57
+ with zipfile.ZipFile(fileobj) as zip_ref:
58
+ zip_ref.extractall(path=self.cache_path)
59
+
60
+ if 'github' in data_url and len(os.listdir(self.cache_path)) == 1:
61
+ # Github inserts one folder at the highest level of the tar file
62
+ # this compensates for this behavior
63
+ gh_url = urlparse(data_url)
64
+
65
+ repo = gh_url.path.split('/')[2]
66
+
67
+ gh_ref = gh_url.path.split('/')[-1]
68
+ if repo.endswith('.git'):
69
+ gh_ref = self.reference
70
+ elif gh_ref.endswith('.tar.gz'):
71
+ gh_ref = gh_ref[0:-7]
72
+ elif gh_ref.endswith('.tgz'):
73
+ gh_ref = gh_ref[0:-4]
74
+ else:
75
+ gh_ref = gh_ref.split('.')[0]
76
+
77
+ if gh_ref.startswith('v'):
78
+ gh_ref = gh_ref[1:]
79
+
80
+ github_folder = f"{repo}-{gh_ref}"
81
+
82
+ if github_folder in os.listdir(self.cache_path):
83
+ # This moves all files one level up
84
+ git_path = os.path.join(self.cache_path, github_folder)
85
+ for data_file in os.listdir(git_path):
86
+ shutil.move(os.path.join(git_path, data_file), self.cache_path)
87
+ os.removedirs(git_path)