Compare commits

...
772 Commits
Author SHA1 Message Date
dependabot[bot] c347d9f84b build(deps-dev): Bump pytest-mock from 3.7.0 to 3.8.1
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.7.0 to 3.8.1.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.7.0...v3.8.1)

---
updated-dependencies:
- dependency-name: pytest-mock
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-30 12:20:05 +05:45
dependabot[bot] efc4787cb0 build(deps-dev): Bump pylint from 2.14.3 to 2.14.4
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.14.3 to 2.14.4.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.14.3...v2.14.4)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-30 11:09:05 +05:45
skshetry be3ec9dc59 deps: bump dvc-data to 0.0.16 2022-06-29 20:16:19 +05:45
dependabot[bot] 685a2d5579 build(deps): Bump styfle/cancel-workflow-action from 0.9.1 to 0.10.0
Bumps [styfle/cancel-workflow-action](https://github.com/styfle/cancel-workflow-action) from 0.9.1 to 0.10.0.
- [Release notes](https://github.com/styfle/cancel-workflow-action/releases)
- [Commits](https://github.com/styfle/cancel-workflow-action/compare/0.9.1...0.10.0)

---
updated-dependencies:
- dependency-name: styfle/cancel-workflow-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-28 09:15:05 +02:00
David de la Iglesia Castro c336507e51 render: image_converter: Support slash in revision.
Fixes #7934
2022-06-27 10:53:03 +02:00
Ruslan Kuprieiev d448eb5fc1 setup: bump dvc-data 2022-06-26 19:40:31 +03:00
skshetry 3dcd0107d2 deps: bump dvc-data to 0.0.13 2022-06-23 14:04:16 +05:45
dberenbaum 2773e99afc config: remove core.jobs dead code 2022-06-22 11:36:01 +08:00
Amey Kulkarni 9a1a3a05f5 Merge pull request #7671 from ap-kulkarni/amey/6141
s3fs: Adds support for SSE client keys
2022-06-22 00:54:10 +03:00
Ruslan Kuprieiev 286fab4aab setup: bump dvc-data to 0.0.12 2022-06-22 00:41:29 +03:00
David de la Iglesia Castro 20f69a121e api: Move to sub-package.
Split into sub-modules.

Split tests.
2022-06-21 20:52:19 +03:00
David de la Iglesia Castro b994c529a2 api: Add params_show.
Closes #6507

Uses `repo.params.show` with custom error_handler and postprocess the outputs for more user-friendly structure.

Extend `repo.params.show` to accept `stages` argument to cover the "params of current stage" use case.
2022-06-21 20:52:19 +03:00
David de la Iglesia Castro f106a68424 params.show: Fix deps for stages using same params file. 2022-06-21 20:52:19 +03:00
David de la Iglesia Castro 66c2e8cbd8 repo.collect: Add duplicates option.
Defaults to `False`.
If `True`, multiple `outs` sharing a provided `target_path` will not be filtered.
2022-06-21 20:52:19 +03:00
Yury 735b563ce4 dvc plots: allow for setting output directory via config
Fixes: #7816
2022-06-20 18:12:12 +02:00
Jorge Orpinel Perez 604e0178f7 readme: make VS Code installation into a separate section.
per https://github.com/iterative/dvc/pull/7916#pullrequestreview-1011575851
2022-06-20 16:10:38 +03:00
Jorge Orpinel Perez f210e41d5a readme: single QS, VSC gif in Install
per https://github.com/iterative/dvc/pull/7916#pullrequestreview-1011465782
and https://github.com/iterative/dvc/pull/7916#pullrequestreview-1011465820
2022-06-20 16:10:38 +03:00
Jorge Orpinel e07e79da52 readme: roll back non-vscode changes 2022-06-20 16:10:38 +03:00
Jorge Orpinel Perez 1284842f70 readme: fix link 2022-06-20 16:10:38 +03:00
Jorge Orpinel Perez 0aa063a1e7 readme: add VS Code Extension sction and gif 2022-06-20 16:10:38 +03:00
Jorge OrpinelandDave Berenbaum 87c5cb7ebf Update README.rst
Co-authored-by: Dave Berenbaum <[email protected]>
2022-06-20 16:10:38 +03:00
Jorge Orpinel Perez 59570c08a4 readme: add VS Code to Installation list + formatting 2022-06-20 16:10:38 +03:00
Jorge Orpinel Perez eafe686dd6 readme: Link current Quick start to Cmd Ref 2022-06-20 16:10:38 +03:00
Jorge Orpinel Perez d056d8a4c5 readme: blocks to link to DVC GS 2022-06-20 16:10:38 +03:00
Jorge Orpinel Perez 165d95be56 readme: intro updates (VSCE) 2022-06-20 16:10:38 +03:00
Ruslan Kuprieiev ea1c54f48e setup: bump dvc-data to 0.0.11 2022-06-20 11:55:50 +03:00
Peter Rowlands 31f67cd3de exp apply: preserve untracked files 2022-06-19 22:41:22 -07:00
Peter Rowlands ee87e2b260 scmrepo: bump to 0.0.25 2022-06-19 22:41:22 -07:00
dependabot[bot] ae61143956 build(deps-dev): Bump pylint from 2.14.2 to 2.14.3
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.14.2 to 2.14.3.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.14.2...v2.14.3)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-20 11:13:19 +05:45
Ruslan Kuprieiev 1253e6cc46 setup: bump dvc-data to 0.0.10 2022-06-19 21:16:08 +03:00
Ruslan Kuprieiev 19f2186c8b setup: bump dvc-data to 0.0.9 2022-06-18 22:34:25 +03:00
Ruslan Kuprieiev 2e21c92c4f dvc: use dvc-data 0.0.8 2022-06-17 22:08:33 +03:00
dberenbaum 4262e26a15 readme: consolidate intro 2022-06-17 10:22:42 +02:00
dependabot[bot] 830c116b4e build(deps-dev): Bump pylint from 2.14.1 to 2.14.2
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.14.1 to 2.14.2.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.14.1...v2.14.2)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-16 10:32:09 +05:45
David de la Iglesia CastroandPeter Rowlands 1663b97eaa api: open: Raise ValueError if rev is used in wrong mode.
Closes #7405

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2022-06-15 18:19:27 +02:00
David de la Iglesia Castro 15e10a1d81 api: open: Add docstring and type hints. 2022-06-15 18:19:27 +02:00
Saugat Pachhai (सौगात) 4bc6b5699a quote node names 2022-06-15 12:43:45 +03:00
dependabot[bot] 8e003e5022 build(deps): Update networkx requirement
Updates the requirements on [networkx](https://github.com/networkx/networkx) to permit the latest version.
- [Release notes](https://github.com/networkx/networkx/releases)
- [Commits](https://github.com/networkx/networkx/compare/networkx-2.5...networkx-2.8.4)

---
updated-dependencies:
- dependency-name: networkx
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-15 12:43:45 +03:00
Saugat Pachhai (सौगात) af649af462 deps: bump dvc-data to 0.0.6; fix imports 2022-06-14 17:50:57 +05:45
dependabot[bot] 957d8d179b build(deps): Bump dvc-render from 0.0.5 to 0.0.6
Bumps [dvc-render](https://github.com/iterative/dvc-render) from 0.0.5 to 0.0.6.
- [Release notes](https://github.com/iterative/dvc-render/releases)
- [Commits](https://github.com/iterative/dvc-render/compare/v0.0.5...0.0.6)

---
updated-dependencies:
- dependency-name: dvc-render
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-14 08:36:27 +05:45
dberenbaum e7b265a178 readme: change vscode to vs code 2022-06-14 00:17:59 +03:00
dberenbaum 422511f66d readme: mention vs code extension 2022-06-14 00:17:59 +03:00
pre-commit-ci[bot] 6826d8ac35 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0)
2022-06-14 00:08:50 +03:00
dependabot[bot] bdea9847b5 build(deps-dev): Bump pylint from 2.13.9 to 2.14.1
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.13.9 to 2.14.1.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.13.9...v2.14.1)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-13 22:15:27 +05:45
Saugat Pachhai (सौगात) 8e6ea87b76 typing: executor: use queue.Queue instead of multiprocessing.Queue 2022-06-13 21:41:54 +05:45
dependabot[bot] 036ac8e7c2 build(deps-dev): Bump mypy from 0.942 to 0.961
Bumps [mypy](https://github.com/python/mypy) from 0.942 to 0.961.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.942...v0.961)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-13 21:41:54 +05:45
dependabot[bot] 3306d2789b build(deps-dev): Bump filelock from 3.7.0 to 3.7.1
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.7.0 to 3.7.1.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.7.0...3.7.1)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-13 20:39:12 +05:45
dependabot[bot] b094839210 build(deps): Bump actions/setup-python from 3 to 4
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-13 20:28:51 +05:45
Ruslan Kuprieiev b9c04a247a dvc: upgrade to dvc-data 0.0.5 2022-06-13 18:03:53 +05:45
Ruslan Kuprieiev ef689c5593 dvc: upgrade to dvc-objects 0.0.4 2022-06-13 18:03:53 +05:45
Saugat Pachhai (सौगात) 7b16a377e5 deps: bump dvc-data to 0.0.3 2022-06-13 18:03:53 +05:45
Daniele Trifirò 793f97bdd5 fix dvc-objects/data imports 2022-06-13 18:03:53 +05:45
Saugat Pachhai (सौगात) d30dbd50bc run-cache: http: stage cache uploads in http remotes
Currently, there is no way to find run-cache in HTTP remotes.
In the future, we could use directory-listings from supported
webservers to find files.
Temporarily, we are restricting the run-cache uploads. This is a bit
strict, we could have only restricted the downloads, but it might be
more confusing to the users, so I went to categorically restrict
stage cache uploads and downloads for http filesystems.
2022-06-12 13:49:56 +03:00
Saugat Pachhai (सौगात) e6db6ffc89 runcache: fix push from Windows to remote filesystems
We were passing paths with '\\' in the filename instead of posix paths to
remote filesystems which was causing failures to upload.
Although in some filesystems, this was failing silently, so upload would seem
to have passed, but the download later would fail.
2022-06-11 11:34:12 +03:00
Ruslan Kuprieiev 9ac2f49b57 Revert "fix dvc-objects/data imports"
This reverts commit 677da2b22c.
2022-06-10 12:01:07 +03:00
Ruslan Kuprieiev 9b2adfd3cc Revert "deps: bump dvc-data to 0.0.3"
This reverts commit 6dd3f1e053.
2022-06-10 12:01:07 +03:00
Saugat Pachhai (सौगात) 6dd3f1e053 deps: bump dvc-data to 0.0.3 2022-06-10 14:07:33 +05:45
Daniele Trifirò 677da2b22c fix dvc-objects/data imports 2022-06-10 14:07:33 +05:45
dberenbaum a3ee993739 remove .github/FUNDING.yml 2022-06-09 08:32:41 +05:45
dberenbaum 95f67445de readme: remove donate requests 2022-06-09 08:32:41 +05:45
Peter Rowlands c9a3cb803c sshfs: bump min ver to 2022.6.0 2022-06-07 05:17:08 -07:00
Daniele Trifirò 41ecd2e204 scm: fix clone
`fetch_all_exp()` in `clone()` was called with `url="origin"`, which
resulted in the operation being performed with the remote URL defined
in the cloned repo's config, which did not include any credentials
initially provided to clone.

Fixes #7670
2022-06-07 05:11:05 -07:00
dberenbaum 33b3afa2f5 exp init: create output dirs 2022-06-07 17:55:24 +05:45
Daniele Trifirò e849162018 exp: speed up repro execution with untracked directories in workspace
When large untracked directories are present in the workspace, a lot of
time is spent collecting untracked files in `scm.status` and `scm.is_dirty`.
2022-06-07 04:43:35 -07:00
Yury c14f96391a checkout: --relink show helpful message on completion 2022-06-07 00:31:42 +02:00
dependabot[bot] 88d3582d40 build(deps): Bump pre-commit/action from 2.0.3 to 3.0.0
Bumps [pre-commit/action](https://github.com/pre-commit/action) from 2.0.3 to 3.0.0.
- [Release notes](https://github.com/pre-commit/action/releases)
- [Commits](https://github.com/pre-commit/action/compare/v2.0.3...v3.0.0)

---
updated-dependencies:
- dependency-name: pre-commit/action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-06-06 04:20:08 -07:00
Ruslan Kuprieiev 8fa0e40669 setup: set upper bound on networkx 2022-06-06 13:31:59 +03:00
Ruslan Kuprieiev a3d6b1299a brancher: use scm.root_dir to determine relative cwd
Fixes #7776
2022-06-06 12:42:22 +03:00
Paweł Redzyński 20c7b0e3e6 plots: grouping: stop using dpath.util.search 2022-06-03 16:48:33 +03:00
huang06 6794dd28f7 docs: update package installation 2022-06-02 17:46:14 +08:00
Peter Rowlands 1cc803d12b exp: output troubleshooting link on shallow merge failure 2022-06-02 01:15:16 -07:00
Ruslan Kuprieiev 2b6aa01392 setup: bump scmrepo to 0.0.24
Fixes #7824
2022-06-01 20:51:08 +03:00
Daniele Trifirò 906c87fdef tests: replace unecessary map() call in test_live
caught by flake8
2022-06-01 20:41:14 +03:00
Daniele Trifirò 52f02d238d bump scmrepo to 0.0.23
`scmrepo==0.0.23` includes proper support for windows file:// URLs
(https://github.com/iterative/scmrepo/pull/68), so Windows tests
relying on file URLs had to be fixed in order to be valid
[RFC 8089](https://datatracker.ietf.org/doc/html/rfc8089) URLs.
2022-06-01 09:20:07 +05:45
David de la Iglesia Castro 3d791778de plots: Pass templates_dir to match_renderers.
We were forgetting to pass default template dir.

Fixes #7817.
2022-05-31 16:17:58 +02:00
Saugat Pachhai (सौगात) 1e77be3ae3 testing: use shutil.copytree with dirs_exist_ok
See https://docs.python.org/3/library/shutil.html#shutil.copytree.
There is dirs_exist_ok kwarg in Python >3.8.
2022-05-30 20:40:31 +05:45
Ruslan Kuprieiev 844f77b4f9 dvc: drop python 3.7
Also upgraded with `pyupgrade --py38-plus`

Fixes #7708
2022-05-30 16:51:35 +03:00
David de la Iglesia Castro 34ef1807be exp: show: Include additional info in --json.
Use relpath to repo.root_dir as keys in deps and outs.
Add `use_cache` and `is_data_source` for outs.

This allows to differentiate git-tracked dependencies and
intermediate outputs.

Closes #7575
Closes #7790
2022-05-25 09:01:39 +02:00
Dave Berenbaum dae0000bcd exp list: fix git_remote metavar 2022-05-24 18:47:26 -07:00
David de la Iglesia Castro 109d0e4e28 dag: mermaid: Use quotation marks to escape node name.
Fixes #7731
2022-05-24 18:27:58 +02:00
Saugat Pachhai (सौगात) 68b0d61c9e tests: move run cache test to test_remote 2022-05-24 10:16:37 +05:45
Saugat Pachhai (सौगात) 942a080567 dvc.stage.cache: fix typo, was using src filesystem to transfer
This was working for LocalFileSystem as they work for any directories/path.
It's hard to test this these days, as we don't have remote tests inside
dvc, so it was not caught.

Potentially fixes #7718.
2022-05-23 16:37:40 +03:00
Saugat Pachhai (सौगात) 60cbcec399 formatting: consider dvc-render as a known first party for ordering imports 2022-05-23 15:49:47 +05:45
Saugat Pachhai (सौगात) 0c5a18513c fix linter issue 2022-05-23 14:12:54 +05:45
Alexander Schepanovski b5c28dd9f9 Catch correct exception class in params.show() 2022-05-23 00:34:34 -07:00
dependabot[bot] 84439e6a04 build(deps): Bump dvc-data from 0.0.1 to 0.0.2
Bumps [dvc-data](https://github.com/iterative/dvc-data) from 0.0.1 to 0.0.2.
- [Release notes](https://github.com/iterative/dvc-data/releases)
- [Commits](https://github.com/iterative/dvc-data/compare/0.0.1...0.0.2)

---
updated-dependencies:
- dependency-name: dvc-data
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-21 20:46:41 +05:45
Saugat Pachhai (सौगात) 75fa487406 dvc: remove unused utils and redundant tests
The tests should be in dvc-objects repo npw, and will be migrated soon.
The nanotime and diskcache are specified in dvc-objects, so we don't need
those here.
2022-05-20 19:17:00 +05:45
Saugat Pachhai (सौगात) 0ecd47b333 deps: remove jaraco.windows dependency
This is used by dvc-objects, and it has the dependency declared.
2022-05-20 15:18:13 +05:45
Saugat Pachhai (सौगात) 9710cafd38 deps: add dvc-objects dependency 2022-05-20 15:13:50 +05:45
Saugat Pachhai (सौगात) e34767ca90 logging: show logs from dvc-data 2022-05-20 15:13:50 +05:45
Saugat Pachhai (सौगात) 9a2feaa4ed dvc: remove dvc.data, fix imports 2022-05-20 15:13:50 +05:45
Saugat Pachhai (सौगात) 7bc98e3fb3 tests: remove objects related tests 2022-05-20 14:02:08 +05:45
Saugat Pachhai (सौगात) 5e561c1dfd deps: add dvc-objects as a dependency 2022-05-20 14:02:08 +05:45
Saugat Pachhai (सौगात) 572b6dfca9 logging: show logs from dvc-objects 2022-05-20 14:02:08 +05:45
Saugat Pachhai (सौगात) 37921d618d dvc: remove dvc.objects, fix imports 2022-05-20 14:02:08 +05:45
Saugat Pachhai (सौगात) 95c6cea36d tests: restructure some tests for objects/data 2022-05-19 18:38:21 +05:45
Saugat Pachhai (सौगात) 455260b29c data/objects: reduce log levels in dvc-objects and dvc-data
Also introduces a `validate_status` kwarg in `data.transfer`,
which is used in dvc to log for missing version info.
2022-05-19 18:33:39 +05:45
Saugat Pachhai (सौगात) 82dc67d890 objects: use separate Tqdm implementations
Now dvc.objects and dvc.data uses the implementation from dvc.objects
for Tqdm progress bars. This is temporary. :)
2022-05-19 18:07:47 +05:45
Saugat Pachhai (सौगात) 2057c471bb data.checkout: remove dependency on DVC; prompts and DvcException 2022-05-19 15:11:27 +03:00
Saugat Pachhai (सौगात) f23d31af64 fs/data: move repo related utils to dvc
This moves get_fs_config/get_fs_cloud and ODBManager to DVC
as they require DVC related utilities, and we don't need them
strictly in dvc-objects/dvc-data.
This also adds support to ReferenceHashFile to load from
any filesystem, using it's module name and filename.
Now we can get rid of DVCFileSystem dependency in
ReferenceHashFile.
This is a temporary solution, we really need a registry.
(I am not quite sure if this works in pyinstaller mode,
but I think it should, it's how fsspec does it if I understand
correctly.)
2022-05-18 19:39:09 +03:00
Saugat Pachhai (सौगात) cf5d3830c5 dvc.objects: use own internal error instead of DvcException
transfer raises internal TransferError, merge raises internal MergeError
and local ODB raises ODBError on unprotect.
transfer's error is reraised to FileTransferError back in DataCloud.
2022-05-18 12:22:57 +03:00
Saugat Pachhai (सौगात) 32cd6c2c03 dvc.objects: use stub ignore in dvc.objects/dvc.data
This commit:
* defines a `Ignore` protocol in `dvc.objects`, that expects a `find` method.
  This will be used in dvc-data and dvc-objects instead of depending on dvc.ignore.
  dvcignore is expected to respect this protocol, which it does.
* renames `dvcignore` kwarg to `ignore` in `dvc-data` and `dvc-objects`.
* teaches dvc.cli that `IgnoreInCollectedError` is a known exception
   and is logged correctly and adds a test for the behaviour.

I am not happy that we need to hardcode `.dvcignore` in dvc-data
but I don't see any easy way, since we always try to avoid
staging if the tree contains .dvcignore file, even if .dvcignore
is not passed.
2022-05-18 12:22:28 +03:00
Saugat Pachhai (सौगात) 54917e3255 dvc.objects: fix typing imports 2022-05-18 11:49:45 +05:45
dependabot[bot] e8a439df8d build(deps): Bump pyinstaller from 5.0 to 5.1
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 5.0 to 5.1.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v5.0...v5.1)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-17 20:15:17 -07:00
Ruslan Kuprieiev 71a4b4823a fs: path: don't overuse parts
These days we have `self.flavour` that is more efficient and more simple.
2022-05-17 22:29:34 +03:00
Ruslan Kuprieiev 01cc97f38a fs: path: use flavour.basename 2022-05-17 20:21:46 +03:00
Saugat Pachhai (सौगात) 3872f79dad objects.fs: do not use format_link, DvcException and fix_env from DVC
* Introduce ConfigError and AuthError in dvc.objects.fs and raise it
  instead of dvc.config.ConfigError.
* Provide hints and links by catching those errors in dvc.cli.main,
  i.e decouple UI messages from filesystems for AuthError and
  ConfigError.
* Copy flatten/unflatten utils to dvc.objects.fs.utils.
2022-05-17 18:23:29 +03:00
Saugat Pachhai (सौगात) 48176b80f6 fs: do not use DvcException in filesystems
* Refactor check_requires, move UI messages to dvc.cli
* Refactor link related error
2022-05-17 16:48:38 +03:00
Saugat Pachhai (सौगात) 8c9a9e9a1f objects.fs: move schemes to objects.fs 2022-05-17 17:02:14 +05:45
Saugat Pachhai (सौगात) 120eb09c4d fs: move GitFileSystem out of dvc.objects.fs
This is more of a DVC thing than objects.
2022-05-17 16:50:33 +05:45
Saugat Pachhai (सौगात) bf7892012d objects.fs: copy executors to dvc.objects 2022-05-17 16:50:21 +05:45
Saugat Pachhai (सौगात) 36605eea4c objects.hash: rename HashedStreamReader to HashStreamFile 2022-05-17 13:25:38 +03:00
Saugat Pachhai (सौगात) 01e5f9eadd objects.hash: use fobj.read instead of read1 2022-05-17 13:25:38 +03:00
Saugat Pachhai (सौगात) 752e10a834 dvc.objects: move HashedStreamReader to objects, use it for calculating fobj_md5
Now HashedStreamReader will be used to calculate md5 in fobj_md5/hash_file.

Also:
* adds in `bytes_md5` that may be useful for testing.
* adds a hasher-registry with hashlib
* adds a `text=` kwarg to be able to specify to use dos2unix or not
  (useful for testing)
* makes `localfs` the default filesystem when `fs` arg is not passed
  (makes it easier to test in the REPL)
2022-05-17 13:25:38 +03:00
Saugat Pachhai (सौगात) 092eda183c objects.fs: use getpass for prompts
Note that this will change the behaviour of DVC to always show
prompt even in --quiet mode, which is what we need.
Previously it'd prompt without any message.
2022-05-17 16:06:27 +05:45
Saugat Pachhai (सौगात) d82e094b13 fs: move filesystems to dvc.objects.fs.implementations 2022-05-16 17:54:57 +03:00
Saugat Pachhai (सौगात) 570e3a5359 dvc.fs: move fs to dvc.objects.fs
Some of the APIs/FileSystems are reimported inside dvc.fs module.
2022-05-16 13:15:19 +03:00
Saugat Pachhai (सौगात) b8e770747f fs.utils: copy dvc.utils.fs to dvc.fs.utils 2022-05-16 11:51:48 +05:45
dependabot[bot] 1999ecf0d3 build(deps-dev): Bump pylint from 2.13.8 to 2.13.9
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.13.8 to 2.13.9.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.13.8...v2.13.9)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-16 08:54:42 +05:45
dependabot[bot] 1ff5f30839 build(deps-dev): Bump filelock from 3.6.0 to 3.7.0
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.6.0...3.7.0)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-15 19:57:31 -07:00
Saugat Pachhai (सौगात) c6ae91c907 dvc.fs: fix iscopy 2022-05-16 08:25:23 +05:45
Saugat Pachhai (सौगात) dc1d3e880a dvc.objects.cache: set type in a more compatible way
It turns out that diskcache.Disk saves passed arguments to the database
and uses that all the time. So this means that we are not forward
compatible as it breaks moving from newer version of DVC to an older
one. We don't promise this, but this breaks our benchmarks which
are probably not isolated enough.

Regarding the use of cache, it is a bit iffy to extend it this way.
We only need this type for error messages. We could consider removing
this, but since the error message is documented and it's not too hard
to extend this :), I decided to preserve the message.
2022-05-13 11:00:31 +05:45
Saugat Pachhai (सौगात) 3f5757a0a5 dvc.objects: move hashing functions to dvc.objects
This commit moves `HashedStreamReader`/`get_hash_file`/`file_md5`
into `dvc.objects.hash`.

The `get_hash_file` has been renamed to `hash_file`.
This also copies `relpath` and `is_exec` to the `dvc.fs.utils`.
2022-05-12 20:33:18 +03:00
Daniele Trifirò 7a795f79db dvc.data: save and try loading raw dir objects
When staging a directory, always save a "raw dir object" to the odb.
If the corresponding ".dir" object has not been added to the odb,
`stage()` calls can load the tree from the raw dir object instead of
rebuilding it by walking the directory.

This can lead to significant speed improvements when calling
`dvc status` for modified directories.

Fixes #7390
2022-05-12 19:27:36 +03:00
skshetry b3fb0dfdad dvc.objects: extend diskcache to handle pickle errors (#7734)
dvc.objects: extend diskcache to handle pickle errors
2022-05-12 21:12:33 +05:45
Saugat Pachhai (सौगात) ee29888e18 dvc.objects: move get_mtime_and_size to dvc.objects.utils 2022-05-12 18:02:42 +03:00
Saugat Pachhai (सौगात) 11eaa87849 add typings to istextfile and system
Also moves umask to dvc.fs.system
2022-05-12 18:02:42 +03:00
Saugat Pachhai (सौगात) b4faa5616f system: change System class to a dvc.fs.system module 2022-05-12 18:02:42 +03:00
Saugat Pachhai (सौगात) 4d6df989f2 dvc.objects: change imports to relative 2022-05-12 18:02:42 +03:00
Saugat Pachhai (सौगात) e585c52187 istextfile: move to dvc.objects 2022-05-12 18:02:42 +03:00
Saugat Pachhai (सौगात) 4add88f627 objects: move System to dvc.fs 2022-05-12 18:02:42 +03:00
Saugat Pachhai (सौगात) aac2fc0ca0 dvc.fs: rename fs.utils to fs.generic
In the spirit of fsspec.generic, it is for cross-filesystem operations.
Trying to avoid `fs.utils` as much as possible, but we have some
functions that are really utilities coming, so I am making a place
for them. :)
2022-05-12 18:02:15 +03:00
Ruslan Kuprieiev 005d56eedf fs: rename RepoFileSystem to DvcFileSystem 2022-05-12 00:39:41 +03:00
Ruslan Kuprieiev b6d25825c8 fs: rename DvcFileSystem to DataFileSystem
Current naming for `DvcFileSystem` and `RepoFileSystem` is confusing, because
dvc is not only about data and it is much more natural to expect something
named `dvcfs` to include complete view for your whole repo (including git
files), which is what `repofs` is doing right now.

`DvcFileSystem` can provide a view not only for the repo data, but also for
external data (e.g. local external outputs). So it makes more sense to call
it a `DataFileSystem`, since it really represents the data management side
of dvc.
2022-05-11 21:49:57 +03:00
dberenbaum 799dca8b67 update value prop in readme 2022-05-11 19:52:11 +03:00
Karthikeyan SingaravelanandDavid de la Iglesia Castro 3faf413288 Use sorted output in test since order is not deterministic.
Co-authored-by: David de la Iglesia Castro <[email protected]>
2022-05-11 18:40:21 +03:00
Karthikeyan Singaravelan a3261637de dag: fix dot file rendering order. 2022-05-11 18:40:21 +03:00
Karthikeyan Singaravelan b8d738220d Revert other tests and test only glob exception. 2022-05-11 18:37:09 +03:00
pre-commit-ci[bot] fb36db450f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-05-11 18:37:09 +03:00
Karthikeyan SingaravelanandDavid de la Iglesia Castro 946ceb288d Update tests/func/test_data_cloud.py
Co-authored-by: David de la Iglesia Castro <[email protected]>
2022-05-11 18:37:09 +03:00
Karthikeyan Singaravelan 8d99ce06aa Move test to test_data_cloud. 2022-05-11 18:37:09 +03:00
Karthikeyan Singaravelan 9b3873c4f0 Move exception raising to glob_targets to cover all commands. 2022-05-11 18:37:09 +03:00
Karthikeyan Singaravelan e177f041c8 Fail on sync when there is no match for glob. 2022-05-11 18:37:09 +03:00
Ruslan Kuprieiev a80a85e6bd dvc: detach gitfs from local paths
Our gitfs paths are currently tied to the local path in which the cloned
repository resides in, which results in a lot of hacky assumptions throughout
the code.

This PR detaches gitfs from local paths completely, making dvc work with a
proper fsspec gitfs implementation.

Part of #7543
2022-05-11 18:17:39 +03:00
Saugat Pachhai (सौगात) 14c9348f93 dvc.objects: move hash_info, state and meta into dvc.objects 2022-05-11 17:35:48 +03:00
karajan1001 7a1f133f29 dvc ls: not raise PathMissingError on empty dir.
fix #5841
2022-05-11 16:38:58 +03:00
Gao 45a01c6a30 Revert "dvc ls: not raise PathMissingError on empty dir."
This reverts commit 09afd2a0cd.
2022-05-11 14:54:13 +03:00
karajan1001 09afd2a0cd dvc ls: not raise PathMissingError on empty dir.
fix #5841
2022-05-11 11:24:32 +03:00
Saugat Pachhai (सौगात) d92c6fed67 fs.callbacks: simplify, ensure None does not break them, lazy richcallbacks 2022-05-10 20:37:21 +05:45
Saugat Pachhai (सौगात) f10dbef0cf TqdmCallback: minor fixup, check for None explicitly
Tqdm supports __bool__, so we should really use `is not None` test
rather than boolean check. This is not used anywhere right now, but
I'd prefer being correct.
2022-05-10 14:22:13 +05:45
Saugat Pachhai (सौगात) 8fb2f65d4b TqdmCallback: make it lazy 2022-05-10 13:41:54 +05:45
dberenbaum 477f266dc5 exp init: fixes #7534; simplifies/updates exp init --live 2022-05-09 20:58:27 +05:45
Ruslan Kuprieiev b14a972cd0 repofs: use / as root_marker
This makes dvcfs/repofs much easier to work with and allows it to support
isabs/abspath/etc correctly, as previously any full path (with root_marker "")
would be impossible to distinguish from a relative one.

This also detaches external workspaces from the main repo workspace in
DvcFileSystem, which was necessary to be able to distinguish dvcfs abspaths
from local external output paths. Note that currently we still have limited
support for those (e.g. get/api supports them, but import doesn't) and this
change keeps it that way.
2022-05-08 17:19:53 +03:00
Saugat Pachhai (सौगात) 562e61235b objects: do not inherit from DvcException 2022-05-06 13:24:41 +03:00
David de la Iglesia Castro 9bd9ac4a37 FileSystem: Handle encoding via kwargs.
Drop if None.

Closes #7691
2022-05-06 02:34:38 +03:00
Saugat Pachhai (सौगात) f89f4ac152 fs.put: introduce non-recursive put 2022-05-05 21:22:29 +03:00
Saugat Pachhai (सौगात) 54dd3ae90c fs.get: use async implementation when supported by the filesystem 2022-05-05 14:15:21 +03:00
dberenbaum 35d2963069 repro/run/stage: reorder options 2022-05-05 16:42:29 +08:00
Samuel Knight 1f5f2f5c97 dvcfile: preserve 'remote' on add and commit
Preserves 'remote' output on dvc add and dvc commit.

Fixes #7356
2022-05-05 08:36:37 +05:45
Saugat Pachhai (सौगात) c64db917f5 fs: add more fsspec methods 2022-05-05 08:16:34 +05:45
Paweł Redzyński 9d476aab8b plots: image converter return absolute paths 2022-05-04 10:57:13 +02:00
Saugat Pachhai (सौगात) 5067e1f5bc fs.utils.transfer: remove tqdm progress bars 2022-05-03 15:15:48 +05:45
Saugat Pachhai (सौगात) 8a0c5680e0 fs: remove download 2022-05-03 14:42:19 +05:45
Saugat Pachhai (सौगात) 61699d1d66 fs: remove upload function 2022-05-03 11:22:21 +05:45
dependabot[bot] 94c776182e build(deps-dev): Bump pytest from 7.1.1 to 7.1.2
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.1 to 7.1.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.1.1...7.1.2)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-03 10:18:39 +05:45
dependabot[bot] e3d3500db8 build(deps-dev): Bump pylint from 2.13.7 to 2.13.8
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.13.7 to 2.13.8.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.13.7...v2.13.8)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-03 09:48:49 +05:45
Saugat Pachhai (सौगात) d0e05c4b66 deps: bump s3fs to 2022.02.0
The callbacks on _get_file/_put_file was only supported on >2022.02.0.
Fixes #7669.
2022-05-02 12:57:00 +03:00
Ruslan Kuprieiev 58d960a47c Revert "fs.utils: add upload that can be conditionally atomic"
This reverts commit dd187df667.
2022-04-30 17:34:39 +03:00
Saugat Pachhai (सौगात) dd187df667 fs.utils: add upload that can be conditionally atomic
The PR also makes odb.add operations atomic, but the checkout is still
not atomic.
2022-04-29 23:02:15 +03:00
Saugat Pachhai (सौगात) b71ea349d2 repofs: only use dvcfs when --dvc-only is specified 2022-04-29 12:03:46 +03:00
karajan1001 c81d841fd7 exp pull/push: better result handling in exp sharing(#7448)
fix: #7448
Current we skip on those experiments which already existed on both sides, but
do not exclude them from the result list. And if we have diverged experiment,
pulling/pushing them will stop the current progress and raise an exception, but
didn't give any information on the condition of other experiments.

1. Change the exp pull and push to match the new API in `scmrepo`.
2. Handle sync status after `exp pull/push` finished.
3. Bump scmrepo to 0.0.19

Co-authored-by:
2022-04-28 19:56:15 +08:00
Saugat Pachhai (सौगात) 127a9c74b2 fs.put_file: fix function signature for size 2022-04-28 15:54:40 +05:45
Saugat Pachhai (सौगात) e30d2fdda5 fs.put_file: don't require size to be passed 2022-04-28 15:31:28 +05:45
Saugat Pachhai (सौगात) ae4f56c5ad fs.put_file: don't require size to be passed 2022-04-28 15:31:28 +05:45
Ruslan Kuprieiev 8cdcf9b8bb utils: remove PathStringTrie 2022-04-27 22:40:38 +03:00
Ruslan Kuprieiev 55a4532f1d ignore: use Trie instead of PathStringTrie 2022-04-27 22:40:38 +03:00
Ruslan Kuprieiev 43b9cab01b repofs: use Trie instead of PathStringTrie 2022-04-27 22:40:38 +03:00
Saugat Pachhai (सौगात) 36d53bd403 dvc.stage: log time duration on stage collection walk in verbose mode 2022-04-27 18:04:14 +03:00
Saugat Pachhai (सौगात) 46fac33ba5 fs: remove unused CHUNK_SIZE var
There is no support for chunk_size kwarg in fs.open.
Also, `upload_fobj` is now able to figure out proper
chunk size from fileobj, so I don't think we need this on _upload_file.
2022-04-27 19:06:46 +05:45
David de la Iglesia Castro 8b68e4fa75 bump dvclive 0.7.3 2022-04-27 18:06:53 +05:45
Saugat Pachhai (सौगात) 4f1fdf0b0c fs.ossfs: remove unused var COPY_POLL_SECONDS 2022-04-27 18:06:24 +05:45
Saugat Pachhai (सौगात) 0d48598e4b fs.ssh: remove unused vars 2022-04-27 17:56:08 +05:45
Saugat Pachhai (सौगात) 5d64f6504f fs.s3: remove custom put_file/get_file 2022-04-27 17:53:17 +05:45
Saugat Pachhai (सौगात) 3d54039614 scm: fix progress bar, show progress and repo being cloned
Before, as we were not passing `total`, the Tqdm progress bar would not
show the bar or not show any progress on the bar. As we get that value lazily,
we should force it to always show the bar.
Also updated the progress bar to say which repo is being cloned.
2022-04-27 16:42:28 +05:45
Saugat Pachhai (सौगात) cda855b97a fs: add cat/cat_file/cat_ranges/pipe/pipe_file/touch methods 2022-04-27 15:57:17 +05:45
David de la Iglesia Castro bea7d56bd5 plots: Fix repo with removed plots
Changes in dvc-render 0.0.5 caused error.
2022-04-27 11:53:30 +02:00
Paweł Redzyński 10bcc9971d plots: tests: integration tests for studio
Related: #7549
2022-04-26 21:32:56 +02:00
Paweł Redzyński 700b648da9 plots: tests: integration test for vscode
Closes: #7549
2022-04-26 21:32:56 +02:00
Saugat Pachhai (सौगात) f84f9cf184 fs: do not pass size to underlying fs.put_file 2022-04-26 18:37:17 +05:45
Saugat Pachhai (सौगात) b53ebc1a5e build: try fixing building package 2022-04-26 17:28:36 +05:45
Saugat Pachhai (सौगात) 31e8eed402 fsspec.callbacks: add rich based callbacks
We have some issues, so we cannot enable them just yet, but will
be good to merge this so that we can play with it.
2022-04-26 12:28:32 +05:45
Saugat Pachhai (सौगात) 112ea1d448 dvc.objects: add callbacks for listing and estimating hashes
This PR gets rid of Tqdm progress bars inside `dvc.objects`.
Those are replaced with multi-phase callback in `hashes_exist`, and
simple callable that takes `completed` value in
`odb._estimate_remote_size()`.

The `odb.list_hashes_exists()` and `odb.all()` now return iterables,
hence the caller has the responsibility of handling progress.

This is now handled in `dvc.data` by using a custom `Tqdm` progressbar.
This is ugly and is temporary, but my motivation is to remove tqdm from dvc.objects
first. We can gradually get rid of these from `dvc.data` too, but
that requires more knowledge about `dvc.data` and involves more
design work. All in time, I guess. :)
2022-04-26 07:38:24 +05:45
Saugat Pachhai (सौगात) f39e35445a fs.upload/download: simplify, remove unused kwargs, callbacks
1. This PR simplifies `fs.upload`/`fs.download`/`fs.download_file`.
2. Removes unused kwargs.
3. `download` uses `dvc.utils.threadpool.ThreadPoolExecutor` which
   supports canceling futures.
4. `download` for directories supports nested/branched callbacks
   and progress bars.
2022-04-26 06:57:10 +05:45
Paweł Redzyński 7211bd02ed fsspec wrapper: remove can handle dirs 2022-04-25 19:48:20 +03:00
Saugat Pachhai (सौगात) 83795ff3df fs.put_file: add support for uploading fileobjects
Also changed the callbacks to inherit from our own so that we can
add few more utilities to it.
2022-04-25 21:23:05 +05:45
Saugat Pachhai (सौगात) 8ead29805f fs: rename getsize to size 2022-04-25 18:52:46 +05:45
Saugat Pachhai fecc81e951 Revert "dvc.objects: add callbacks for listing and estimating hashes"
This reverts commit 7599c6e20d.
2022-04-25 18:37:05 +05:45
Saugat Pachhai (सौगात) 7599c6e20d dvc.objects: add callbacks for listing and estimating hashes
This PR gets rid of Tqdm progress bars inside `dvc.objects`.
Those are replaced with multi-phase callback in `hashes_exist`, and
simple callable that takes `completed` value in
`odb._estimate_remote_size()`.

The `odb.list_hashes_exists()` and `odb.all()` now return iterables,
hence the caller has the responsibility of handling progress.

This is now handled in `dvc.data` by using a custom `Tqdm` progressbar.
This is ugly and is temporary, but my motivation is to remove tqdm from dvc.objects
first. We can gradually get rid of these from `dvc.data` too, but
that requires more knowledge about `dvc.data` and involves more
design work. All in time, I guess. :)
2022-04-25 18:36:47 +05:45
Saugat Pachhai (सौगात) 68a3cd9a13 fs: unify fs.base.FileSystem with FSSpecWrapper
Since all of our filesystems are fsspec-compliant, there's no need for compat wrapper.
2022-04-25 16:01:06 +09:00
David de la Iglesia Castro a394498067 render.match: Skip errored revisions in get_files 2022-04-22 16:44:52 +02:00
Saugat Pachhai (सौगात) 7708a8e292 gitfs: add regression test for broken stage.collect on gitfs when cwd != repo_root 2022-04-21 21:03:08 +05:45
Saugat Pachhai (सौगात) 23e62a9ff7 fs: make localfs fsspec-compliant
It is not completely fsspec compliant, but it's upto the level
required by our FSSpecWrapper.

Next thing to tackle would be to merging `fs.base.FileSystem`
and `fs.fsspec_wrapper.FSSpecWrapper` into one filesystem
that is lazily-loaded and is itself fsspec-compliant.
2022-04-21 12:44:55 +03:00
Saugat Pachhai (सौगात) fed0bfb2b3 tests: add test for dvc.api.open with remote
Covers #7341
2022-04-21 15:12:01 +05:45
Saugat Pachhai (सौगात) f22da81999 tests: make xfail-marked-tests to be strict by default 2022-04-21 15:06:47 +05:45
pre-commit-ci[bot] 22a988b113 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-04-21 15:06:31 +05:45
joncrall 77a8e870ff Add log statement to collect repo 2022-04-21 15:06:31 +05:45
David de la Iglesia Castro f20d6a7048 dag: Add --mermaid option. 2022-04-21 14:03:45 +05:45
Saugat Pachhai (सौगात) 6ffba62b75 executors: add support for cancel_futures=True in Python<3.9
Also adds a kwarg `cancel_on_error` on the constructor to support
cancelling futures on `shutdown`. This is not done by default to
be backward compatible with `concurrent.futures.ThreadPool` executor.
2022-04-21 14:03:10 +05:45
David de la Iglesia Castro 5c561572b0 exp init: Replace live section with metrics and plots.
Closes #7534
2022-04-21 08:55:49 +02:00
Peter Rowlands 444bf27e6f exp show: add internal --no-fetch option to disable temp/queue ref fetch 2022-04-21 15:45:59 +09:00
Peter Rowlands ba82faee53 exp show: don't acquire repo lock 2022-04-21 15:45:59 +09:00
dependabot[bot] 72d57f13bc build(deps-dev): Bump pylint from 2.13.5 to 2.13.7
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.13.5 to 2.13.7.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.13.5...v2.13.7)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-04-21 08:22:47 +05:45
Saugat Pachhai (सौगात) 02b2ab6804 stage/exp list: alias --names-only to --name-only
Fixes #7570.
2022-04-21 06:53:16 +05:45
Ruslan Kuprieiev fd440df5b3 dvcignore: use fs.path instead of os.path 2022-04-20 12:27:26 +03:00
Saugat Pachhai (सौगात) cc8a0ce134 completion: decouple completion related stuff from command
It also now uses the main parser itself that invokes the command rather than
creating a new parser object.
2022-04-20 13:29:33 +05:45
Ivan Shcheklein 5c1e7be909 bump pydrive: improves and fixes thread safety 2022-04-20 06:06:28 +05:45
David de la Iglesia Castro 179e080c2f hdfs: Fix _strip_protocol and _checksum
Match behavior previous to #6946
2022-04-19 16:22:49 +03:00
Saugat Pachhai (सौगात) f3a7ec7f48 completion: fix dvc exp completion 2022-04-19 17:28:22 +05:45
Saugat Pachhai (सौगात) c0580c740b fs: move NoDirectoriesMixin to http filesystem 2022-04-19 11:54:34 +05:45
dependabot[bot] b4dcadbc9c build(deps-dev): Bump pylint from 2.12.2 to 2.13.5 (#7518)
build(deps-dev): Bump pylint from 2.12.2 to 2.13.5
2022-04-19 11:35:10 +05:45
Ruslan Kuprieiev 55219e9089 fs: get rid of CallbackMixin in azure/gs/hdfs/webhdfs
No longer needed, as all of those filesystems already support callbacks.
2022-04-18 17:34:06 +03:00
David de la Iglesia Castro 70b57d4703 live: Remove html generation
Remove commands/live.
Remove LiveTask.
Bump dvclive.
2022-04-18 15:47:31 +02:00
Saugat Pachhai (सौगात) 5eb16ab05e parsing: use relative paths; force absolute paths to relative 2022-04-18 14:19:56 +03:00
Ruslan Kuprieiev 517f45d45f repofs: migrate to fsspec 2022-04-18 14:17:20 +03:00
Ruslan Kuprieiev 53c7f44480 repofs: rename _main_repo -> repo 2022-04-18 14:17:20 +03:00
Ruslan Kuprieiev 9d75e8261b repofs: use repo paths 2022-04-18 14:17:20 +03:00
Ruslan Kuprieiev b3b0ec1fba repofs: unify ls/walk/info/exists/etc behaviour 2022-04-18 14:17:20 +03:00
Ruslan Kuprieiev 0a3f5cbdd5 repofs: base walk on ls 2022-04-18 14:17:20 +03:00
Ruslan Kuprieiev bdd21fd481 repofs: use fs.ls 2022-04-18 14:17:20 +03:00
Ruslan Kuprieiev 394118ef3d repofs: don't assume repofs paths are the same as underlying fs paths 2022-04-18 14:17:20 +03:00
Ruslan Kuprieiev f500c66aef repofs: use fs.walk directly 2022-04-18 14:17:20 +03:00
dependabot[bot] 318235f072 build(deps): Bump pyinstaller from 4.10 to 5.0
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 4.10 to 5.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v4.10...v5.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-04-18 14:13:52 +08:00
Peter Rowlands ab395dfb7a exp show: prefer exp names for experiments 2022-04-15 14:02:35 +09:00
Nick SorrosandPaweł Redzyński dd9bc116a3 Update test name to be more informative
Co-authored-by: Paweł Redzyński <[email protected]>
2022-04-13 18:23:14 +02:00
Nick Sorros 0a3dd7a97f Move test to output and use mocker 2022-04-13 18:23:14 +02:00
Nick Sorros 5da72fc8ff Patch get_remote_odb instead 2022-04-13 18:23:14 +02:00
Nick SorrosandPaweł Redzyński d8dac6f50d Reraise RemoteMissingDepsError as is
Co-authored-by: Paweł Redzyński <[email protected]>
2022-04-13 18:23:14 +02:00
Nick Sorros 36c27ed17f Add test for remote missing when adding data dir 2022-04-13 18:23:14 +02:00
pre-commit-ci[bot] 43f5faa652 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-04-13 18:23:14 +02:00
Nick Sorros 968f9b38bf Raise informative missing s3 extra 2022-04-13 18:23:14 +02:00
pre-commit-ci[bot] aab7f8af4d [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.1.0 → v4.2.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.1.0...v4.2.0)
2022-04-12 10:46:46 +09:00
Paweł Redzyński 79c87ba119 Update setup.cfg 2022-04-09 15:45:29 +03:00
dependabot[bot] aa5cfc46ed build(deps): Bump scmrepo from 0.0.16 to 0.0.17
Bumps [scmrepo](https://github.com/iterative/scmrepo) from 0.0.16 to 0.0.17.
- [Release notes](https://github.com/iterative/scmrepo/releases)
- [Commits](https://github.com/iterative/scmrepo/compare/0.0.16...0.0.17)

---
updated-dependencies:
- dependency-name: scmrepo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-04-09 15:45:29 +03:00
Peter Rowlands 69acbe33c9 erepo: include exp refs in clones
- add test for dvc get --rev=<exp>
2022-04-09 10:27:06 +09:00
David de la Iglesia Castro 7dd0cffd49 gdrive: Add test_service_account_using_env_var 2022-04-08 21:39:03 +02:00
RCdeWit a11aac37ea Process feedback @skshetry and @karajan1001 2022-04-08 21:39:03 +02:00
Rob de WitandLaurens Duijvesteijn 6de1ddadf5 fs.gdrive: Fix GDRIVE_CREDENTIALS_DATA for service accounts
`_validate_config()` had a bug: it would not take into account that Google
Drive credentials can also be passed using `GDRIVE_CREDENTIALS_DATA` as
documented here [1].

This patches the validation logic to also accept this environment variable as a
means to provide service account credentials.

Before, commands like `dvc push` would exit with:

```
ERROR: failed to push data to the cloud - To use service account, set
  `gdrive_service_account_json_file_path`, and
  optionally`gdrive_service_account_user_email` in DVC config
Learn more at <https://man.dvc.org/remote/modify>
```

[1]: https://dvc.org/doc/user-guide/setup-google-drive-remote#authorization

Co-authored-by: Laurens Duijvesteijn <[email protected]>
2022-04-08 21:39:03 +02:00
Fabian ZillsandSaugat Pachhai 7804e6550e Update dvc/repo/index.py
Co-authored-by: Saugat Pachhai <[email protected]>
2022-04-07 21:05:47 +05:45
Fabian ZillsandSaugat Pachhai 65d935e5c7 Update dvc/repo/index.py
Co-authored-by: Saugat Pachhai <[email protected]>
2022-04-07 21:05:47 +05:45
Fabian 44af5b3b7e index: add getitem
* allow access to a stage via `getitem` in addition to iterating over the Index.
2022-04-07 21:05:47 +05:45
Paweł Redzyński 03f553c54b plots: json: show even if no results
Related: iterative/vscode-dvc#1521
2022-04-07 11:45:01 +02:00
dependabot[bot] 1e0a4a26eb build(deps): Bump codecov/codecov-action from 2.1.0 to 3
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 2.1.0 to 3.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v2.1.0...v3)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-04-06 17:54:18 +02:00
Peter Rowlands ee0a47f166 cli: check for closed stdout/stderr 2022-04-05 22:05:22 +09:00
Peter Rowlands fa7b371c74 daemon: skip setting PYTHONPATH in pyinstaller frozen binary 2022-04-05 22:05:22 +09:00
David de la Iglesia Castro 117b22d58a exp show: pcp: Allow to --drop Experiment 2022-04-04 11:48:55 +02:00
Paweł Redzyński bfb1b74923 plots: image: fix image to_json structure 2022-04-01 12:26:20 +02:00
Ruslan Kuprieiev d600eeca99 dvcfs: base on fsspec 2022-03-31 17:55:18 +03:00
Ruslan Kuprieiev 0470e164cc repofs: use dvcfs.ls
Using ls instead of walk reduces potential problems that may occur if
dvcfs.walk happens to yield results in different order than
gitfs/localfs.walk.
2022-03-31 17:55:18 +03:00
Ruslan Kuprieiev 0ab86c8fae dvcfs: implement ls
On our way to making dvcfs fsspec-compliant.
2022-03-31 17:55:18 +03:00
Saugat Pachhai (सौगात) 067442f348 webdav: consider path to be root
Remove _strip_protocol, moves to get_cloud_fs loading stage.
2022-03-31 17:35:23 +03:00
Saugat Pachhai (सौगात) b58ebff5db lint: fix variable reassignment bug while iterating from flake8 2022-03-31 13:41:25 +05:45
Saugat Pachhai (सौगात) 565a813e2a setup.cfg: tests: try to remove more dependencies 2022-03-31 13:36:43 +05:45
Saugat Pachhai (सौगात) dc0bc0c535 setup.cfg: tests: remove unnecessary dependencies 2022-03-31 12:29:29 +05:45
Jorge Orpinel Perez 65397517ca pre-commit: fix black/ click incompatibility issue
per https://github.com/iterative/dvc/pull/7524#discussion_r839189136
2022-03-31 12:14:35 +05:45
Peter Rowlands 1a3a222239 scmrepo: update to 0.0.16 2022-03-30 15:46:18 +09:00
Saugat Pachhai (सौगात) 5ff1d76826 ui: add revision info on dvc.yaml/dvc.lock/.dvc syntax and schema errors 2022-03-29 17:49:03 +05:45
dependabot[bot] 582c4afc38 build(deps): Bump scmrepo from 0.0.13 to 0.0.14
Bumps [scmrepo](https://github.com/iterative/scmrepo) from 0.0.13 to 0.0.14.
- [Release notes](https://github.com/iterative/scmrepo/releases)
- [Commits](https://github.com/iterative/scmrepo/compare/0.0.13...0.0.14)

---
updated-dependencies:
- dependency-name: scmrepo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-29 17:37:37 +05:45
dependabot[bot] b1284cb4af build(deps-dev): Bump hdfs from 2.6.0 to 2.7.0
Bumps [hdfs](https://github.com/mtth/hdfs) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/mtth/hdfs/releases)
- [Changelog](https://github.com/mtth/hdfs/blob/master/CHANGES)
- [Commits](https://github.com/mtth/hdfs/commits)

---
updated-dependencies:
- dependency-name: hdfs
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-29 16:23:01 +05:45
Saugat Pachhai (सौगात) 7d0d6a38d7 exp init: change ui prompts structure and output 2022-03-29 16:20:32 +05:45
dependabot[bot] 700654cb1e build(deps): Bump dvc-render from 0.0.3 to 0.0.4
Bumps [dvc-render](https://github.com/iterative/dvc-render) from 0.0.3 to 0.0.4.
- [Release notes](https://github.com/iterative/dvc-render/releases)
- [Commits](https://github.com/iterative/dvc-render/compare/v0.0.3...v0.0.4)

---
updated-dependencies:
- dependency-name: dvc-render
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-29 10:02:54 +02:00
dependabot[bot] 5a7790dcd5 build(deps-dev): Bump mypy from 0.931 to 0.942
Bumps [mypy](https://github.com/python/mypy) from 0.931 to 0.942.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.931...v0.942)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Bump mypy from 0.931 to 0.942

after mypy 0.940, it disabled all class scoped imports because it will
cause crashes.

1. upgrade mypy from 0.931 to 0.942
2. Solve the error `class scoped import` in mypy 0.942
2022-03-29 10:05:08 +05:45
David de la Iglesia Castro ead7724824 render: vega_converter: make rev and filename optional.
Add `FILENAME_FIELD`.
2022-03-28 19:34:05 +02:00
David de la Iglesia Castro ddd6c97f71 render: Extract to dvc_render
Create and use separate package for rendering logic.
Closes #6944
2022-03-28 19:34:05 +02:00
Saugat Pachhai (सौगात) b71a0f30fb s3: add aiobotocore as dependency explicitly
Fixes #7257 and fixes #7475.
2022-03-28 19:48:55 +05:45
dependabot[bot] 9930551458 build(deps-dev): Bump pytest from 6.2.5 to 7.1.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.5 to 7.1.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.2.5...7.1.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-28 18:39:25 +05:45
Saugat Pachhai 3322980ff6 bump pytest-docker to 0.11.0
This adds support for pytest>7.
See https://github.com/avast/pytest-docker/pull/74.
2022-03-28 18:17:59 +05:45
dependabot[bot] 23a86c9a15 build(deps-dev): Bump types-requests from 2.27.14 to 2.27.15
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.14 to 2.27.15.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-28 18:03:05 +05:45
Daniele Trifirò 57419273ea fs.http: set sock_read timeout 2022-03-28 08:20:56 +02:00
Daniele Trifirò 879c9bd7d6 fs.http: force cleanup of closed connections 2022-03-28 08:20:56 +02:00
Saugat Pachhai d09d39538a Revert "build: pin setuptools to <61"
This reverts commit c08aa4a93b.
2022-03-26 07:26:51 +05:45
Casper da Costa-Luis b684b2b25a deps: bump tqdm
- fixes #7465
2022-03-25 19:16:33 +05:45
dependabot[bot] 7de443d8bc build(deps-dev): Bump types-requests from 2.27.13 to 2.27.14
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.13 to 2.27.14.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-25 14:24:45 +02:00
Saugat Pachhai (सौगात) d008054ea2 objects.db: get rid of callbacks from internal _list_paths API
This removes callback from internal APIs: `_list_paths`, `list_hashes`,
and `_hashes_with_limit`. These are all iterators and hence can just be
replaced by wrapping a progressbar over those iterators in the caller
side.

We still have progressbars in `hashes_exist`, `_estimate_remote_size`,
`_list_hashes_traverse` and `list_hashes_exists`, which should gradually
lifted up and replaced.
2022-03-25 14:13:26 +02:00
Saugat Pachhai (सौगात) b92bc3e01a fs.webdav: consider url/path to be root
Webdav filesystem expects path to be relative
to the base url, instead of having full prefix.

So we are setting it to "" (root) through `_strip_protocol`
which converts fully-qualified name to the file-system
specific path.
2022-03-25 13:56:33 +02:00
Saugat Pachhai (सौगात) c08aa4a93b build: pin setuptools to <61
A workaround for https://github.com/pypa/setuptools/issues/3196.
2022-03-25 16:13:49 +05:45
Gao 81eee05995 UI problem in dvc exp push 2022-03-22 20:25:04 +09:00
karajan1001andDave Berenbaum 325ead5e8b exp remove: show removed experiments #7449
fix: #7449
`exp remove`: should show the experiments that were removed

1. experiment.remove now returns the experiment list been removed.
2. modify the functional test to these the returned list.
3. use ui to show the removed list.

Co-authored-by: Dave Berenbaum <[email protected]>
2022-03-22 17:13:44 +08:00
dberenbaum 5cf59e4ee6 exp: remove mentions of resume 2022-03-22 14:07:56 +09:00
dependabot[bot] 290a593322 build(deps): Bump actions/cache from 2.1.7 to 3
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.7...v3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-22 10:32:12 +08:00
Peter Rowlands 19dcba9db6 erepo/exp: use explicit fetch + merge instead of pull
- tests: use mocker.spy instead of patch(wraps=...)
2022-03-21 18:17:36 +09:00
Peter Rowlands ab54b5bdfc scmrepo: update to 0.0.13 2022-03-21 18:17:36 +09:00
Peter Rowlands 2e21be992d tests: fix incorrect git URLs 2022-03-21 18:17:36 +09:00
Saugat Pachhai (सौगात) 9d96ddfbe7 exp init: create params file by default
Note that this won't add support for tracking the params file,
it'll only create the params file. Some changes on error and
prompts are made.
2022-03-21 15:00:20 +05:45
Saugat Pachhai (सौगात) a3bab48f41 params: allow tracking file 2022-03-21 13:47:44 +05:45
dependabot[bot] a13f2770ef build(deps-dev): Bump types-tabulate from 0.8.5 to 0.8.6
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.5 to 0.8.6.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-tabulate
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-18 10:28:30 +05:45
dependabot[bot] 079714772c build(deps-dev): Bump types-requests from 2.27.12 to 2.27.13
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.12 to 2.27.13.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-17 12:45:54 +01:00
dependabot[bot] cd65400d83 build(deps-dev): Bump google-cloud-storage from 2.2.0 to 2.2.1
Bumps [google-cloud-storage](https://github.com/googleapis/python-storage) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/googleapis/python-storage/releases)
- [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-storage/compare/v2.2.0...v2.2.1)

---
updated-dependencies:
- dependency-name: google-cloud-storage
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-16 13:09:21 +01:00
dependabot[bot] 0d06a98c67 build(deps-dev): Bump google-cloud-storage from 2.1.0 to 2.2.0
Bumps [google-cloud-storage](https://github.com/googleapis/python-storage) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/googleapis/python-storage/releases)
- [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-storage/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: google-cloud-storage
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-15 12:31:05 +08:00
dependabot[bot] 3fc8dcfc59 build(deps-dev): Bump types-requests from 2.27.11 to 2.27.12
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.11 to 2.27.12.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-14 10:44:03 +01:00
Saugat Pachhai (सौगात) e80e725fe4 tests: fix test failures with rich==12 2022-03-11 19:26:54 +05:45
karajan1001 6a6955e27c exp show/pull/push/list: help text for -n (#7444)
fix: #7444

from the discussion https://github.com/iterative/dvc.org/pull/3187#discussion_r821382100

1. Update the help message of `-n`
2022-03-10 17:11:42 +08:00
karajan1001andJorge Orpinel efc021da73 exp remove: rename --all-commits flag and unify the collection of revs (#7155)
fix: #7155
1. rename flags `-A/--all-commits` in `exp remove`
2. add new flag `-n/--num` in `exp remove`
3. unify the revision collection in `exp remove`
4. add unit and func tests for `exp remove`

Co-authored-by: Jorge Orpinel <[email protected]>
2022-03-09 18:54:15 +08:00
karajan1001 244637e299 exp push/pull: add --all-commits flag and unify the collection of revs (#7154)
fix: #7154
1. rename flags `-A/--all-commits` in the `exp pull/push`
2. add new flag "--rev" and "--num" in the `exp pull/push`
3. Unify the collection of revs in `exp push/pull`
4. add unit and func tests for `exp push/pull`
2022-03-09 17:43:26 +08:00
karajan1001andJorge Orpinel d033345860 exp list: add the new flags (#7153)
fix: #7153
Unify the flags in `exp list` to `exp show`

1. Add new `-A`, `-n` flags to `exp list` command.
2. Add unit test for the flag parse.
3. unify the revs retrive logic to `exp show`.
4. Add functional test for `exp list`
5. Update the documents.

Co-authored-by: Jorge Orpinel <[email protected]>
2022-03-08 16:17:33 +08:00
Lucas Cabral de390419a6 fix: hooks install alongside pre-commit tool 2022-03-08 13:16:16 +05:45
Peter Rowlands d80c43135f exp run: remove unsupported (repro only) arguments 2022-03-07 12:01:51 +01:00
David de la Iglesia Castro 5249207ceb live: Remove summary from tests 2022-03-07 10:09:04 +01:00
dependabot[bot] 2ebf9cb9b9 build(deps-dev): Bump dvclive[image] from 0.5.1 to 0.6.0
Bumps [dvclive[image]](https://github.com/iterative/dvclive) from 0.5.1 to 0.6.0.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.5.1...0.6.0)

---
updated-dependencies:
- dependency-name: dvclive[image]
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-07 10:09:04 +01:00
dependabot[bot] 17899bc109 build(deps): Bump pyinstaller from 4.9 to 4.10
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 4.9 to 4.10.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/v4.10/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v4.9...v4.10)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-07 15:29:18 +09:00
dependabot[bot] 380c65134e build(deps): Bump scmrepo from 0.0.7 to 0.0.10
Bumps [scmrepo](https://github.com/iterative/scmrepo) from 0.0.7 to 0.0.10.
- [Release notes](https://github.com/iterative/scmrepo/releases)
- [Commits](https://github.com/iterative/scmrepo/compare/0.0.7...0.0.10)

---
updated-dependencies:
- dependency-name: scmrepo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-07 12:12:56 +05:45
dependabot[bot] ab024a47b3 build(deps): Bump actions/checkout from 2.4.0 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.4.0...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-03 15:05:39 +08:00
dberenbaum fc3f3ca8d7 diff: fix missing key bug (#6720) 2022-03-02 19:10:31 +01:00
David de la Iglesia Castro 6bb3794f52 Suppress --live-no-summary help. 2022-03-02 13:30:19 +01:00
dependabot[bot] a2e25cf449 build(deps): Bump actions/setup-python from 2 to 3
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 3.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-01 17:30:52 +09:00
Peter Rowlands 59b8498905 fs: remove unused connection pool module 2022-03-01 15:36:49 +09:00
Zhenyu Gao a378e017e1 Fix dvc add -o issue #7357 2022-02-28 15:24:16 +01:00
dependabot[bot] 4c71aadc21 build(deps-dev): Bump filelock from 3.5.1 to 3.6.0
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.5.1 to 3.6.0.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.5.1...3.6.0)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-28 15:21:02 +01:00
Paweł Redzyński 3b4d76df42 plots: json: add datapoints to vega plots 2022-02-25 17:12:45 +01:00
dependabot[bot] 6c04d0efd1 build(deps-dev): Bump types-requests from 2.27.10 to 2.27.11
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.10 to 2.27.11.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-23 12:31:15 +05:45
David de la Iglesia Castro f41651c97c Remove outdated SSH default jobs. 2022-02-22 12:34:25 +01:00
Saugat Pachhai (सौगात) fcddf080da exp init: refactor and simplify interactive mode
- Moves workspace tree for interactive mode after prompts. Previously
  this was displayed before the prompts.
- Remove the yaml contents and the confirmation prompt in an interactive
  mode.
- Simplify `init_interactive()`.
2022-02-22 16:59:08 +05:45
Ruslan Kuprieiev 82b0a3088f dvcfs: handle repo root
Fixes #7407
2022-02-21 04:23:43 +02:00
Ruslan Kuprieiev 30fa3fc41b fs: walk: get rid of onerror
Makes dvc/repofs and other filesystems comply with fsspec.
2022-02-18 16:17:46 +02:00
Ruslan Kuprieiev d8fa55bafa fs: git: move isfile/walk to fsspec wrapper 2022-02-18 12:48:13 +02:00
Daniele Trifirò 220c633497 status: cleanup invocation of hashes_exist 2022-02-18 02:37:48 +02:00
Daniele Trifirò 3767b03784 test_data_cloud: also check for the correct number of jobs 2022-02-18 02:37:48 +02:00
Daniele Trifirò c0f63efe99 fix passing of jobs kwarg
the `jobs` kwarg was not being passed down properly,
resulting in it being ignored in some cases (push/pull/gc)
2022-02-18 02:37:48 +02:00
dependabot[bot] 0b18efb7b7 build(deps-dev): Bump filelock from 3.4.2 to 3.5.1
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.4.2 to 3.5.1.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.4.2...3.5.1)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-17 15:05:37 +02:00
Saugat Pachhai (सौगात) 497eac952c s/Creating/Created 2022-02-17 14:18:08 +05:45
Saugat Pachhai (सौगात) 6eb5c31dd7 fix UI message 2022-02-17 14:18:08 +05:45
Saugat Pachhai (सौगात) 69ce69e427 exp init: update message 2022-02-17 14:18:08 +05:45
Saugat Pachhai (सौगात) f8467cf375 exp init: log created dependencies in non-interactive mode 2022-02-17 14:18:08 +05:45
Saugat Pachhai (सौगात) 2735fbac71 exp init: create workspace structure by default, show workspace tree 2022-02-17 14:18:08 +05:45
dependabot[bot] 795fe24a5f build(deps-dev): Bump types-requests from 2.27.9 to 2.27.10
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.9 to 2.27.10.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-16 12:02:54 +08:00
karajan1001 d1f7cf4540 exp show: show exp when checkpoint is running(#7329)
fix: #7329
In a previous PR, we introduce a bug which use `HEAD` as the workspace
commit, while if there is a checkpoint is running, the `HEAD` is
actually changing all the time, we should use
`refs/exps/exec/EXEC_BASELINE` instead.

1. add `fix_exp_head` transfer in `dvc/scm.py::resolve_rev`
2. remove fix_exp_head from `diff`, `metrics diff`, `params diff` and
   `plots diff`
3. modify a functional test to simulate the running conditions
2022-02-15 21:17:42 +08:00
dependabot[bot] d5b809d705 build(deps-dev): Bump types-toml from 0.10.3 to 0.10.4
Bumps [types-toml](https://github.com/python/typeshed) from 0.10.3 to 0.10.4.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-toml
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-11 08:28:58 +05:45
Daniele Trifirò 4db9d19387 destroy: also remove dvcignore file 2022-02-10 16:16:12 +01:00
David de la Iglesia Castro 31a3461465 live: Update tests 2022-02-10 16:46:32 +05:45
dependabot[bot] e6d52ac594 build(deps-dev): Bump dvclive[image] from 0.5.0 to 0.5.1
Bumps [dvclive[image]](https://github.com/iterative/dvclive) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.5.0...0.5.1)

---
updated-dependencies:
- dependency-name: dvclive[image]
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-10 16:46:32 +05:45
dependabot[bot] a78d63cf58 build(deps-dev): Bump types-requests from 2.27.8 to 2.27.9
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.8 to 2.27.9.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-10 15:52:07 +08:00
David de la Iglesia Castro d89555cd31 exp show: handle renaming of metrics/params.
_extend_row did not considered partially empty `items`, which can occur when metris or params are renamed between commits.

Fixes #7070
2022-02-09 21:40:15 +01:00
Nikita Kodenko b6fa1178e7 tests: migrate unittest to pytest 2022-02-08 18:51:17 +09:00
Ruslan Kuprieiev e430693fc8 dvcfs: use paths relative to the repo root
Currently we use both absolute and relative (depend on cwd) paths, which is
terrible for an api, as their interpretation depends on external factors.
This PR makes dvcfs accept only paths relative to the repo root for regular
outputs and absolute paths for external outputs.

Pre-requisite for making repofs use same relative paths.
2022-02-07 20:12:33 +02:00
dependabot[bot] e60ab00f51 build(deps): Bump pyinstaller from 4.8 to 4.9
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 4.8 to 4.9.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/v4.9/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v4.8...v4.9)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-07 18:02:45 +05:45
Ruslan Kuprieiev cb584c6a53 dvcfs: detach from pipeline outputs
Introducing simple repo tree that combines all workspaces into one structure,
and allows us to detach data representation from pipelines.

Pre-requisite for dvc-data extraction and using relpaths in dvcfs/repofs.
2022-02-07 13:12:52 +02:00
Ruslan Kuprieiev 9b5ccb7554 Revert "ls: don't use outs"
This reverts commit 27b090709b.
2022-02-04 11:04:00 +02:00
Ruslan Kuprieiev 27b090709b ls: don't use outs
ls doesn't support subrepos, so we can simply use dvcfs in --dvc-only.
2022-02-03 23:59:29 +02:00
Ruslan Kuprieiev 48641fb41f repofs: don't use outs
Using outs directly breaks isolation and makes it really hard to understand
what's going on.

Redundant abspaths will go away in followups when moving to relpaths.
2022-02-03 23:33:37 +02:00
Ruslan Kuprieiev 8497edab93 dvcfs: use info() instead of metadata()
metadata was introduced before our migration to fsspec and no longer needed
now. This is a pre-requisite to using data index in dvcfs/repofs.

Note that a bunch of awkward flags like isdvc/isout/etc are still kept, and
will be removed in followups.
2022-02-03 22:45:10 +02:00
Saugat Pachhai dc13179759 Delete scripts/schema directory
https://github.com/iterative/dvcyaml-schema provides the canonical schema,
this is not used anywhere and is stale.
2022-02-01 20:35:07 +05:45
David de la Iglesia Castro 37cfb8e8af exp show: Sort deps columns.
Closes #7312
2022-02-01 15:23:26 +01:00
Paweł Redzyński 7bc14a5ddb plots: make templates generation explicit
Fixes: #6389
2022-02-01 15:09:25 +01:00
dependabot[bot] 22ebfd1e2f build(deps-dev): Bump pytest-mock from 3.6.1 to 3.7.0
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.6.1 to 3.7.0.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.6.1...v3.7.0)

---
updated-dependencies:
- dependency-name: pytest-mock
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-01 19:53:26 +05:45
pre-commit-ci[bot] d213ccb686 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-02-01 19:53:13 +05:45
pre-commit-ci[bot] 0ce1f982b0 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/ambv/black: 21.12b0 → 22.1.0](https://github.com/ambv/black/compare/21.12b0...22.1.0)
2022-02-01 19:53:13 +05:45
dependabot[bot] a4b3da6acc build(deps-dev): Bump types-requests from 2.27.7 to 2.27.8
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.7 to 2.27.8.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-01 19:24:56 +05:45
dependabot[bot] b58a01cfd5 build(deps-dev): Bump dvclive[image] from 0.4.6 to 0.5.0
Bumps [dvclive[image]](https://github.com/iterative/dvclive) from 0.4.6 to 0.5.0.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.4.6...0.5.0)

---
updated-dependencies:
- dependency-name: dvclive[image]
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-02-01 14:24:23 +01:00
Daniele Trifirò 0562b4fd5f tests: update test_exceptions for rich 11.1.0 behaviour 2022-02-01 13:20:07 +01:00
tapadipti 95731e9a37 Doc URL correction for GitAuthError 2022-01-31 14:50:37 +05:45
Ruslan Kuprieiev aca6d229da output: move isexec to meta
With our staging process automatically capturing file metadata, we no longer
need to set isexec ourselves.

In followup PRs we will also use meta during checkout, so that we won't need
to use Output.set_exec by hand.
2022-01-27 18:55:32 +02:00
dependabot[bot] 60e502d361 build(deps-dev): Bump google-cloud-storage from 2.0.0 to 2.1.0
Bumps [google-cloud-storage](https://github.com/googleapis/python-storage) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/googleapis/python-storage/releases)
- [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-storage/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: google-cloud-storage
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-27 14:48:46 +05:45
David de la Iglesia Castro 8c2f8095da debug: Remove test_viztracer_internal 2022-01-27 09:36:06 +05:45
David de la Iglesia Castro 5e80fcce4f exp show: Include outs in --json output. 2022-01-26 18:45:48 +01:00
David de la Iglesia Castro 1a1e28282d exp show: Include deps columns.
Use `repo.index.deps` to collect dependencies associated with each experiment.
2022-01-26 18:45:48 +01:00
Daniele Trifirò e3b97cb3a7 ignore: move debug statetement to the proper location 2022-01-26 18:44:01 +02:00
David de la Iglesia Castro d882e21b9b debug: Remove viztracer-depth default 2022-01-25 20:28:41 +05:45
David de la Iglesia Castro 3276c76a02 tests: Add test_debug 2022-01-25 20:28:41 +05:45
Saugat Pachhai 758dc43575 Update dvc/_debug.py 2022-01-25 20:28:41 +05:45
David de la Iglesia Castro f1dc9e50a7 debug: Add support for viztracer profiler. 2022-01-25 20:28:41 +05:45
Saugat Pachhai (सौगात) 2f15f30b0e exp run: do not log message twice on workspace executor
Fixes #7276.
2022-01-25 19:01:48 +05:45
Peter Rowlands 47d10c4a8b utils: add decorator to wrap diskcache pickle errors 2022-01-25 16:21:53 +09:00
David de la Iglesia Castro 822b501069 add: Update help of --jobs 2022-01-24 21:06:47 +01:00
Saugat Pachhai (सौगात) 943481f0a3 exp init: handle exception properly on missing parameters file 2022-01-24 17:43:29 +05:45
Jeff Hale b30385885b Fix 3 README links so that they render in GitHub 2022-01-24 15:55:12 +09:00
David de la Iglesia Castro d4861d535a TabularData: Simplify drop internals.
Update `subset` type hint.
2022-01-22 09:41:29 +08:00
dberenbaum 646097a735 update exp init help text 2022-01-21 11:07:32 +05:45
David de la Iglesia Castro 43953ace53 exp show: Add --drop and --keep args
Removed `--include-metrics` / `--include-params` and
`--exclude-metrics` / `--exclude-params`.

Removed `--no-timestamp` . Can be done by `--drop Created`.

`--drop` and `--keep` operate directly on the table columns.

`--keep` does not perform any filtering. It's only used
to specify columns to keep despite the other filtering.

For example `--only-changed --keep foo` will prevent
`foo` from being removed by `--only-changed`.

Another example, `--drop train.* --keep train.dropout`
will remove all columns matching `train.*` except for
`train.dropout`.

Closes #7079
Closes #7080
Pre-requisite for #6434
2022-01-20 03:28:33 +01:00
David de la Iglesia Castro 255e6a1aec TabularData: Support protected`columns.
TabularData: Add subset arg to dropna and drop_duplicates.

Closes #7083
2022-01-20 03:28:33 +01:00
Saugat Pachhai (सौगात) 73fee513b9 cli: improve startup speed
fsspec callbacks were making us lose 35ms (~207 ms vs 170ms now).
These are now extracted from dvc.progress to dvc.fs._callback.
2022-01-19 20:38:19 +02:00
Saugat Pachhai (सौगात) b0f65cbd17 tests: increase timeout for exp init tests 2022-01-19 20:26:41 +05:45
Saugat Pachhai (सौगात) 672f079cc6 tests: clean repositories before each test runs 2022-01-19 20:21:48 +05:45
Saugat Pachhai (सौगात) 446516d788 refactor: rename dvc.command to dvc.commands 2022-01-19 19:41:40 +05:45
Saugat Pachhai (सौगात) 1b3e1251e6 cli refactor: move dvc.command.base to dvc.cli.command 2022-01-19 19:41:40 +05:45
Saugat Pachhai (सौगात) 4268b5a339 cli: don't inherit DvcParserError from DvcException, move to dvc.cli 2022-01-19 19:41:40 +05:45
Saugat Pachhai (सौगात) f5baf990cf cli: refactor into a module
By splitting dvc.cli into a module, we'll be able to share cli-related
utilities, split parser-related stuffs into smaller parts, improve
reusability and improve API for command extensions in the future.
2022-01-19 12:22:07 +02:00
dependabot[bot] 69ff760e4b build(deps-dev): Bump pytest-timeout from 2.0.2 to 2.1.0
Bumps [pytest-timeout](https://github.com/pytest-dev/pytest-timeout) from 2.0.2 to 2.1.0.
- [Release notes](https://github.com/pytest-dev/pytest-timeout/releases)
- [Commits](https://github.com/pytest-dev/pytest-timeout/compare/2.0.2...2.1.0)

---
updated-dependencies:
- dependency-name: pytest-timeout
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-19 10:57:17 +05:45
Saugat Pachhai (सौगात) 4ba504be3e repro/exp run: display stage instead of filepath on failure
Before it used to say 'failed to reproduce dvc.yaml', which is now changed
to print 'failed to reproduce train' or something similar.

For stages that are in dvc.yaml in the current working directory, it will
just print the stage names. For stages in other dvc.yaml files, it'll print

in <relative_path>:<stage_name>.
2022-01-19 06:58:40 +05:45
Ruslan Kuprieiev 377b0bc689 find_root: unify error message
We used to use that message for special external repo cases, but we don't
need it anymore.

Fixes #7281

Leftover from #7214
2022-01-19 01:39:48 +02:00
Matt Seddon 89f2d42df8 plots: update linear template so colors can be externally overwritten 2022-01-18 12:42:17 +01:00
karajan1001 57263f15fa Pull: accept multiple as input(#7196)
fix: #7196

New feature: now `dvc exp pull` can accept a list of experiment names as input.

2. add support to list input for the `exp pull`
3. add function tests for the multi exp pull
4. add unit test for the arg parse
2022-01-18 17:31:55 +08:00
karajan1001 9bb6494e55 Push: accept multiple as input(#7196)
fix: #7196

New feature: now `dvc exp push` can accept a list of experiment names as input.

1. refactor of experiment push
2. add support to list input for the `exp push`
3. add function tests for the multi exp push
4. add unit test for the arg parse
2022-01-18 17:31:55 +08:00
Ruslan Kuprieiev 13238e9716 objects: fix imports 2022-01-15 21:39:39 +02:00
Ruslan Kuprieiev 85181e396f objects: fix empty line 2022-01-15 21:20:09 +02:00
Ruslan Kuprieiev 4c3c492b90 objects: don't forget __init__.py 2022-01-15 21:16:20 +02:00
dependabot[bot] 474812195e build(deps-dev): Bump wsgidav from 3.1.1 to 4.0.1
Bumps [wsgidav](https://github.com/mar10/wsgidav) from 3.1.1 to 4.0.1.
- [Release notes](https://github.com/mar10/wsgidav/releases)
- [Changelog](https://github.com/mar10/wsgidav/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mar10/wsgidav/compare/v3.1.1...v4.0.1)

---
updated-dependencies:
- dependency-name: wsgidav
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-14 09:12:16 +05:45
Saugat Pachhai c83612be71 deps-dev: don't specify pygments explicitly
pygments is a transitive dependency for us, from `rich` and here it was for `collective.checkdocs`.
2022-01-14 09:11:20 +05:45
dependabot[bot] 161e7892ba build(deps-dev): Bump google-cloud-storage from 1.43.0 to 2.0.0
Bumps [google-cloud-storage](https://github.com/googleapis/python-storage) from 1.43.0 to 2.0.0.
- [Release notes](https://github.com/googleapis/python-storage/releases)
- [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-storage/compare/v1.43.0...v2.0.0)

---
updated-dependencies:
- dependency-name: google-cloud-storage
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-14 09:02:38 +05:45
dependabot[bot] 0a0e3c3f9c build(deps-dev): Bump types-requests from 2.27.5 to 2.27.7
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.5 to 2.27.7.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-14 09:02:25 +05:45
dependabot[bot] ac2feb2340 build(deps): Bump pyinstaller from 4.7 to 4.8
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 4.7 to 4.8.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES-3.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v4.7...v4.8)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-13 17:00:03 +09:00
Peter Rowlands dd5d999644 exp: add exp run --machine flag 2022-01-13 16:34:20 +09:00
Peter Rowlands 3c7695b39d exp: add SSHExecutor.reproduce via exp exec-run 2022-01-13 16:34:20 +09:00
Peter Rowlands 367e5d1429 exp: add exec-run plumbing command to run exp from ExecutorInfo 2022-01-13 16:34:20 +09:00
Peter Rowlands fb16cd06e3 machine: update behavior for exp ssh executors
- log startup script finished message
- add setup_script option for runtime env setup
2022-01-13 16:34:20 +09:00
Peter Rowlands 39eee9b5a6 exp: implement git + DVC cache collection for ssh executors 2022-01-13 16:34:20 +09:00
Peter Rowlands 84da78a62b exp: initial SSHExecutorManager implementation 2022-01-13 16:34:20 +09:00
Peter Rowlands 4b9d39c901 push/fetch: support push/fetch to specific ODB
- prerequisite for remote execution
- allow explicitly pushing imports which would normally be skipped
  (internal-use only, does not apply to `dvc push`)
2022-01-13 16:34:20 +09:00
David de la Iglesia Castro 5b8bf52eab plots: Fix --json requiring -o.
`-o` was being required even when renderers don't need output path.
2022-01-12 18:00:08 +01:00
karajan1001andPeter Rowlands b673ac24d0 exp show: add --rev flag (#7152)
fix: #7152
1. Add `--rev` flag to `dvc exp show`.
2. Add -1 support for `--num` flag`.
3. Extract revision found logic to `utils`.
4. Brancher use this revision found logic.
5. Update command unit test
6. Add a util unit test.

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2022-01-11 19:01:34 +08:00
Saugat Pachhai (सौगात) 87abeaddc2 rich-table: fix add_column args/kwargs
Rich added `vertical` kwarg in `add_column`, which linter
is now complaining (as it should). But this may keep on breaking
when rich adds/removes args/kwargs in the future.

So, this commit uses (*args, **kwargs) instead of specifying particular
parameters explicitly. This also sets `collapse` directly to the
`rich.table.Column` rather than extending through our one `Column`
implementation.
2022-01-11 11:28:54 +05:45
dependabot[bot] a3dece8236 build(deps-dev): Bump types-tabulate from 0.8.4 to 0.8.5
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.4 to 0.8.5.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-tabulate
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-11 09:19:36 +05:45
dependabot[bot] 221adf246b build(deps-dev): Bump types-toml from 0.10.1 to 0.10.3
Bumps [types-toml](https://github.com/python/typeshed) from 0.10.1 to 0.10.3.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-toml
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-11 09:10:44 +05:45
dependabot[bot] 8681086bfb build(deps-dev): Bump mypy from 0.930 to 0.931
Bumps [mypy](https://github.com/python/mypy) from 0.930 to 0.931.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.930...v0.931)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-11 09:02:39 +05:45
dependabot[bot] 6c3b1370a3 build(deps-dev): Bump types-requests from 2.26.3 to 2.27.5
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.3 to 2.27.5.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-11 08:36:12 +05:45
Ruslan Kuprieiev 9a01d98876 odb: move gc into a separate function 2022-01-11 01:52:34 +02:00
Ruslan Kuprieiev e190433249 lint: add new ignores
mypy:

```
dvc/utils/table.py: note: In member "add_column" of class "Table":
dvc/utils/table.py:24:5: error: Argument 7 of "add_column" is incompatible with
supertype "Table"; supertype defines the argument type as
"Union[Literal['top'], Literal['middle'], Literal['bottom']]"  [override]
        def add_column(  # pylint: disable=arguments-differ
        ^
dvc/utils/table.py:24:5: note: This violates the Liskov substitution principle
dvc/utils/table.py:24:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
dvc/utils/table.py:24:5: error: Argument 8 of "add_column" is incompatible with
supertype "Table"; supertype defines the argument type as
"Union[Literal['fold'], Literal['crop'], Literal['ellipsis'], Literal['ignore']]"
 [override]
        def add_column(  # pylint: disable=arguments-differ
        ^
dvc/utils/table.py:24:5: error: Argument 12 of "add_column" is incompatible
with supertype "Table"; supertype defines the argument type as "Optional[int]"
[override]
        def add_column(  # pylint: disable=arguments-differ
        ^
Found 3 errors in 1 file (checked 129 source files)
dvc/ui/table.py:87: note: In module imported here,
dvc/compare.py:25: note: ... from here:
dvc/utils/table.py: note: In member "add_column" of class "Table":
dvc/utils/table.py:24:5: error: Argument 7 of "add_column" is incompatible with
supertype "Table"; supertype defines the argument type as
"Union[Literal['top'], Literal['middle'], Literal['bottom']]"  [override]
        def add_column(  # pylint: disable=arguments-differ
        ^
dvc/utils/table.py:24:5: note: This violates the Liskov substitution principle
dvc/utils/table.py:24:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
dvc/utils/table.py:24:5: error: Argument 8 of "add_column" is incompatible with
supertype "Table"; supertype defines the argument type as
"Union[Literal['fold'], Literal['crop'], Literal['ellipsis'], Literal['ignore']]"
 [override]
        def add_column(  # pylint: disable=arguments-differ
        ^
dvc/utils/table.py:24:5: error: Argument 12 of "add_column" is incompatible
with supertype "Table"; supertype defines the argument type as "Optional[int]"
[override]
        def add_column(  # pylint: disable=arguments-differ
        ^
Found 3 errors in 1 file (checked 128 source files)
```

pylint:

```
************* Module dvc.utils.table
dvc/utils/table.py:24:4: W0237: Parameter 'vertical' has been renamed to 'overflow' in overridden 'Table.add_column' method (arguments-renamed)
dvc/utils/table.py:24:4: W0237: Parameter 'overflow' has been renamed to 'width' in overridden 'Table.add_column' method (arguments-renamed)
dvc/utils/table.py:24:4: W0237: Parameter 'width' has been renamed to 'min_width' in overridden 'Table.add_column' method (arguments-renamed)
dvc/utils/table.py:24:4: W0237: Parameter 'min_width' has been renamed to 'max_width' in overridden 'Table.add_column' method (arguments-renamed)
dvc/utils/table.py:24:4: W0237: Parameter 'max_width' has been renamed to 'ratio' in overridden 'Table.add_column' method (arguments-renamed)
dvc/utils/table.py:24:4: W0237: Parameter 'ratio' has been renamed to 'no_wrap' in overridden 'Table.add_column' method (arguments-renamed)
dvc/utils/table.py:24:4: W0237: Parameter 'no_wrap' has been renamed to 'collapse' in overridden 'Table.add_column' method (arguments-renamed)
```
2022-01-11 00:47:38 +02:00
Ruslan Kuprieiev f64018b75e dvc: separate data and objects
Initial simple separation to get the process of extracting dvc-objects
and dvc-data packages out of dvc.

dvc-objects is for:
 * defining base Object (aka HashFile) and ObjectDB
 * providing tools to work with those objects (check/transfer/etc)
 * etc

dvc-data is for:
 * defining higher level objects (e.g. Tree)
 * bulding objects from the workspace (aka staging)
 * projecting objects back into the workspace (aka checkout)
 * comparing Tree objects (aka diff)
 * defining misc object databases (e.g LocalObjectDB)
 * higher-level transfer (e.g. with Tree indexes)
 * etc

For example, the objects we use in run-cache are based on dvc-objects
and not on dvc-data, as they have absolutely nothing to do with data
management.

There are still some mixups in the code that will be addressed in the
followups.
2022-01-11 00:47:38 +02:00
Gavin Li 92f714e5ca fs: test_links: create destination directory first
`dvc.fs.utils.test_links()` fails with a CacheLinkError if the target `to_path` does not already exist, as is the case when "abc.dvc" exists (referring to a directory object) and we try to run "dvc pull abc/def". To avoid this issue, fully create `to_path` before attempting to create a link in it.
2022-01-08 13:56:51 +02:00
Jon Burdo 634246f81c add tests for Path.parts 2022-01-07 13:26:45 +02:00
Jon Burdo 05b5e9dc29 handle paths ending with sep in dvc.fs.path.Path.parts 2022-01-07 13:26:45 +02:00
Paweł Redzyński a3144482ba render: vega: data processing outside of renderers
Fixes: #6943
2022-01-07 10:39:12 +01:00
Amrit GhimireandSaugat Pachhai 44a1146915 test: fix test failure in case of different default branch (#6857)
* test: fix test failure in case of different default branch

When the git config has set global `init.defaultBranch` different than master,
a few test cases were failing.
Currently, the test case is assuming the default branch to be set as
master.

This commit will introduce **initial_branch** option to git init call
which will ensure *master* branch is used as default for out test cases.

Set default git branch to main for test CI

Since main is going to be new default branch, it will let the test case
handle the scenario where default branch is set other than master.

Introduce isolation for git configuration

Co-authored-by: Saugat Pachhai (सौगात) <[email protected]>
2022-01-07 11:13:04 +05:45
Saugat Pachhai (सौगात) ec3600eb8e output: don't raise RemoteCacheRequiredError on instantiation, make it lazy 2022-01-06 22:29:49 +02:00
Paweł Redzyński fe4fefd880 ls: handle broken symlink
Fixes: #7120
2022-01-06 22:12:17 +02:00
karajan1001andPeter Rowlands b6a2402d27 exp remove: refactor exp utils (#7196)
fix: #7196

1. Refactor exp_ref_by_name in `dvc/repo/experiments/utils.py`.

In previous the exp_ref_by_name walk throught all of the references to
match only one exp name. This makes multi exp name seeking to be a job
of `O(mn)` complexity, Walking though the exp ref and return all of the
exp ref with greatly reduce this complexity to `O(n)`

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2022-01-06 12:20:29 +08:00
Saugat Pachhai d90fe54df4 tests: remove tests.unit.scm
I forgot to remove it before during #6836.
2022-01-05 20:36:29 +05:45
Peter Rowlands 9e590edb70 odb: pin index pickle protocol version 2022-01-04 20:22:08 +02:00
karajan1001 6bb4889022 ui: remove None in CellT type
From https://github.com/iterative/dvc/pull/6893/files#r777784748
Table rendering does not support `None` for now, and we will change it
back after (#7167).
2022-01-04 15:41:18 +08:00
David de la Iglesia Castro 0a4958bafd TabularData: Decouple pcp from render. 2022-01-03 16:30:54 +01:00
David de la Iglesia Castro 5a9f861921 exp show: Rename html to pcp
Closes #7165
2022-01-03 16:30:54 +01:00
David de la Iglesia Castro fc1ab7d3a1 Update show.py 2022-01-03 16:14:37 +01:00
David de la Iglesia Castro 760c8fceb3 plots: Add auto_open hint 2022-01-03 16:07:23 +01:00
David de la Iglesia Castro d8e2c14927 config: Add plots.auto_open.
Option for enabling automatically open webbrowser with generated html.
Defaults False.

Closes #6091
2022-01-03 16:07:23 +01:00
dependabot[bot] a73accd071 build(deps-dev): Bump filelock from 3.4.0 to 3.4.2
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.4.0 to 3.4.2.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.4.0...3.4.2)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-03 20:04:58 +05:45
dependabot[bot] 69ac43f131 build(deps-dev): Bump pylint-plugin-utils from 0.6 to 0.7
Bumps [pylint-plugin-utils](https://github.com/PyCQA/pylint-plugin-utils) from 0.6 to 0.7.
- [Release notes](https://github.com/PyCQA/pylint-plugin-utils/releases)
- [Commits](https://github.com/PyCQA/pylint-plugin-utils/compare/0.6...0.7)

---
updated-dependencies:
- dependency-name: pylint-plugin-utils
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-03 17:36:22 +05:45
Alexander Schepanovski 9ee428ef17 Fix memory leak in Index.build_graph()
`@memoize()` memory lives as long as decorated function lives, which is
permanent in this case. Since `self` is referenced as a memory key no
`Index` will ever be freed.

Changed to use `@cached_property` whose cache lives as long as the
parent instance.
2022-01-03 13:41:10 +02:00
Peter Rowlands 0e0e1725e6 exp: handle non-exp refs in get_exact_name() 2022-01-03 15:02:19 +05:45
Saugat Pachhai (सौगात) a1609cfb10 exp: raise InvalidExpRefError properly
Before it was just left instantiated, but was not raised.
2022-01-03 15:02:19 +05:45
David de la Iglesia Castro 175ffaeb22 exp show: Remove alternating background 2022-01-03 16:38:39 +09:00
dependabot[bot] 025e15d500 build(deps-dev): Bump types-tabulate from 0.8.3 to 0.8.4
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.3 to 0.8.4.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-tabulate
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-03 10:38:08 +05:45
dependabot[bot] 52a234c5b8 build(deps-dev): Bump pygments from 2.10.0 to 2.11.1
Bumps [pygments](https://github.com/pygments/pygments) from 2.10.0 to 2.11.1.
- [Release notes](https://github.com/pygments/pygments/releases)
- [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES)
- [Commits](https://github.com/pygments/pygments/compare/2.10.0...2.11.1)

---
updated-dependencies:
- dependency-name: pygments
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-03 10:09:53 +05:45
Ruslan Kuprieiev 8ec1af1cf4 config: remove useless os.path.join 2021-12-31 19:03:05 +02:00
Ruslan Kuprieiev 35d364a527 repo: use fs instead of scm
SCM is used for both reading and writing operations, but in the former case -
as filesystem and in the latter - as actual scm/git. So we should distinguish
those and use fs directly, which follows the overall philosophy of being able
to use Repo on any fs.

Keeping `rev=` option for `Repo` because it is a very convenient way to get
repo instance from local path but for particular branch/tag/etc.

Pre-requisite for git/dvc/repofs refactorings.
2021-12-31 02:44:21 +02:00
David de la Iglesia Castro 9a5614d326 TabularData: Add ignore_empty flag to drop_duplicates.
Configures whether to consider missing values as relevant or not.
2021-12-29 21:33:36 +01:00
David de la Iglesia Castro 12a737d905 TabularData: Fix drop_duplicates for rich_text NaN.
When filling missing values with `ui.rich_text` (i.e. in experiments show CMD), those values were not being correctly matched against `self._fill_value`)
2021-12-29 21:33:36 +01:00
Paweł Redzyński 51a6116b92 plots: dir: handle no data in cache/remote
Fixes: iterative/cse#4
2021-12-29 13:21:37 +01:00
dependabot[bot] 3566b1941c build(deps-dev): Bump types-requests from 2.26.2 to 2.26.3
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.2 to 2.26.3.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-29 12:53:59 +09:00
pre-commit-ci[bot] ceeda6d307 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.1.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.1.0)
2021-12-28 15:01:38 +08:00
karajan1001 98d7f785e9 exp show: remove a useless function
The function `_raise_error_if_all_disabled` is copyed wrongly to this
place during sub command refactoring. It used to be used in `exp gc`,
can be deleted from this function.
2021-12-27 20:35:07 +01:00
Ruslan Kuprieiev 04e93da0bd fs: remove unused RemoteCmdError 2021-12-23 19:02:44 +02:00
Ruslan Kuprieiev 72aaca4343 odb: move verify option to config
These options are part of the default config for each particular remote.
Plugins will define their default config on theirown, but for now we keep
those in one config schema.
2021-12-23 12:49:16 +02:00
dependabot[bot] 60cb8322d9 build(deps-dev): Bump mypy from 0.921 to 0.930
Bumps [mypy](https://github.com/python/mypy) from 0.921 to 0.930.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.921...v0.930)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-23 09:50:23 +05:45
dependabot[bot] b0b9a74207 build(deps-dev): Bump wheel from 0.37.0 to 0.37.1
Bumps [wheel](https://github.com/pypa/wheel) from 0.37.0 to 0.37.1.
- [Release notes](https://github.com/pypa/wheel/releases)
- [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst)
- [Commits](https://github.com/pypa/wheel/compare/0.37.0...0.37.1)

---
updated-dependencies:
- dependency-name: wheel
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-23 09:50:05 +05:45
Saugat Pachhai (सौगात) 32fcc73078 deps: explicitly specify pyarrow for hdfs 2021-12-22 16:45:12 +02:00
Saugat Pachhai (सौगात) e3596178d4 deps: move requests-kerberos to webhdfs_kerberos extras instead of webhdfs 2021-12-22 16:44:52 +02:00
Saugat Pachhai (सौगात) ba40d91de6 fs.hdfs: skip port if not available 2021-12-22 16:43:56 +02:00
dependabot[bot] 02cfbcfc32 build(deps-dev): Bump mypy from 0.920 to 0.921
Bumps [mypy](https://github.com/python/mypy) from 0.920 to 0.921.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.920...v0.921)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-22 12:45:02 +02:00
dependabot[bot] 96acb64fec build(deps): Bump scmrepo from 0.0.6 to 0.0.7
Bumps [scmrepo](https://github.com/iterative/scmrepo) from 0.0.6 to 0.0.7.
- [Release notes](https://github.com/iterative/scmrepo/releases)
- [Commits](https://github.com/iterative/scmrepo/compare/0.0.6...0.0.7)

---
updated-dependencies:
- dependency-name: scmrepo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

tests: fix invalid set_ref in exp func test
2021-12-22 17:52:30 +09:00
David de la Iglesia Castro 1507a6ed99 Update benchmarks.yaml 2021-12-21 09:03:19 +05:45
Saugat Pachhai 6034f0e36a s3: remove explicit dependency on aiobotocore; simplify constraints
Hoping to fix `dvc[s3]` installations in pipenv. Pipenv's resolver is in loop, backtracking ruamel.yaml from 0.17.17 to 0.17.11 continuously. I think this has to do
more with `s3`'s dependency than `ruamel.yaml` as it only fails here. And, we are specifying dependency in 1.0.1 whereas aiobotocore has released 2.0 versions which is what is being installed, so it's redundant anyway. I am hopeful that this may help resolver with the simplification in constraints.
2021-12-20 14:32:42 +05:45
Saugat Pachhai 6df1ee87ab Specify bcrypt explicitly 2021-12-20 12:16:14 +05:45
dependabot[bot] a6263cffc8 build(deps-dev): Bump types-requests from 2.26.1 to 2.26.2
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.1 to 2.26.2.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-20 10:37:39 +08:00
Paweł Redzyński 8ca3a7b684 fs: repo: raise proper FileNotFoundError in metadata
Related: #7120
2021-12-17 15:48:29 +02:00
David de la Iglesia Castro 4d5be46ab4 exp show --html: Handle fill_value.
Adds new argument `fill_value` to ParallelCoordinatesRenderer.

For scalar columns, prevent converting to categorical by explictly inserting None when value is missing (value == fill_value).

For categorical columns, replace `fill_value` with `"Missing"` and ensure it is the last item so it gets rendered on top of the column.
2021-12-17 14:45:12 +09:00
David de la Iglesia Castro 5ee82d4000 exp show: Add parallel coordinates plot.
Uses `TabularData.to_parallel_coordinates`.
Adds new arguments: `html`, `out`, `open`

Reuses `--sort-by` to define colorscale.

Closes #4455
2021-12-17 14:45:12 +09:00
David de la Iglesia Castro a5b7c5ea19 TabularData: Add to_parallel_coordinates.
Uses `ParallelCoordinatesRenderer` and `dvc.render.html.write`.

pre-requisite #4455
2021-12-17 14:45:12 +09:00
David de la Iglesia Castro 19b3c82698 dvc render: Add plotly parallel coords renderer
New renderer based on plotly. Not exposed to `dvc plots`.
Generate plotly datapoints from `TabularData`.

pre-requisite #4455
2021-12-17 14:45:12 +09:00
David de la Iglesia Castro 9bd60f62d7 exp show: Add alternating row colors and update border style.
Use `box.HORIZONTALS`.

rich_table: Support `box.HORIZONTALS`

Closes #7128
2021-12-17 14:31:53 +09:00
dependabot[bot] 970cbfa64e build(deps-dev): Bump mypy from 0.910 to 0.920
Bumps [mypy](https://github.com/python/mypy) from 0.910 to 0.920.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.910...v0.920)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-16 16:04:47 +09:00
Ruslan Kuprieiev 0bf56fe29b rename master -> main 2021-12-15 22:19:36 +02:00
Ruslan Kuprieiev 30d6839a5f diff: use relpaths
Regression from path_info removal.

Also fixes #7147
2021-12-15 17:27:38 +02:00
Paweł Redzyński e24de3a0f3 plots: introduce --json option
Fixes: #7073
2021-12-15 11:05:57 +01:00
Peter Rowlands 25ef812ecf exp: use process manager dirs for serialized executors
- partially refactor existing pidfile behavior into using proc manager
- old pidfiles are now json-serialized executor info files placed in
  proc manager subdirs (alongside proc manager pidfile/logs/etc)
- move multiprocessing based (attached) execution into separate
  execution path from future "detached" execution support
2021-12-15 15:55:32 +09:00
Peter Rowlands fa819b0b70 exp: refactor workspace repro into executor classes 2021-12-15 15:55:32 +09:00
Peter Rowlands 37f8010b42 exp: initial executor manager refactor
- introduce ExecutorManager classes
- move executor init + execution from experiments.__init__ into the
  manager classes
- make executors serializable via ExecutorInfo
2021-12-15 15:55:32 +09:00
Saugat Pachhai 8319cc50c6 Update setup.cfg 2021-12-14 21:11:28 +05:45
dependabot[bot] 98c22e3277 build(deps): Bump scmrepo from 0.0.4 to 0.0.5
Bumps [scmrepo](https://github.com/iterative/scmrepo) from 0.0.4 to 0.0.5.
- [Release notes](https://github.com/iterative/scmrepo/releases)
- [Commits](https://github.com/iterative/scmrepo/compare/0.0.4...0.0.5)

---
updated-dependencies:
- dependency-name: scmrepo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-14 21:11:28 +05:45
Saugat Pachhai (सौगात) 3ca8e26169 tests: get rid of GitFS related tests
Also refactored unittest-based tests for LocalFileSystem to pytest
2021-12-14 17:55:26 +05:45
karajan1001andPeter Rowlands 0deab0118a exp: split exp sub command (#5489)
related to #5489
Current dvc/command/experiments.py is too long for switch between cmd class
and the argument parser.

Split the sub command in dvc/command/experiments.py to individual files

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2021-12-14 19:16:04 +08:00
dependabot[bot] 72af8b8605 build(deps-dev): Bump pytest-timeout from 2.0.1 to 2.0.2
Bumps [pytest-timeout](https://github.com/pytest-dev/pytest-timeout) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/pytest-dev/pytest-timeout/releases)
- [Commits](https://github.com/pytest-dev/pytest-timeout/compare/2.0.1...2.0.2)

---
updated-dependencies:
- dependency-name: pytest-timeout
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-14 16:53:58 +08:00
Saugat Pachhai (सौगात) ba4955738f tests: remove func.test_scm 2021-12-13 21:03:24 +05:45
dependabot[bot] 602713e57b build(deps-dev): Bump pytest-xdist from 2.4.0 to 2.5.0
Bumps [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/pytest-dev/pytest-xdist/releases)
- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.4.0...v2.5.0)

---
updated-dependencies:
- dependency-name: pytest-xdist
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-13 13:39:50 +09:00
Paweł Redzyński 7250e1b336 dvc: fix installation from archives
Fixes: #7124
2021-12-11 05:49:28 +02:00
Ruslan Kuprieiev 896bbf1fc6 fs: get rid of unused DETAIL_FIELDS 2021-12-10 18:02:22 +02:00
Ruslan Kuprieiev 81eda7e2b7 fs: get rid of CHECKSUM_DIR_SUFFIX and is_dir_hash
These are leftovers from old remotes, that clearly have nothing
to do with filesystems.
2021-12-10 10:53:10 +02:00
Ruslan Kuprieiev 82c5caee27 setup: unpin s3fs/ossfs/gcsfs 2021-12-09 15:20:22 +02:00
Paweł Redzyński eb41b8b3e0 setup: bump gcsfs
Related: #7053
2021-12-09 15:20:22 +02:00
Ruslan Kuprieiev ea49fb95f7 setup: bump s3fs
Fixes #7053
2021-12-09 13:57:40 +02:00
dependabot[bot] e977bcc6d9 build(deps-dev): Bump dvclive[image] from 0.4.5 to 0.4.6
Bumps [dvclive[image]](https://github.com/iterative/dvclive) from 0.4.5 to 0.4.6.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.4.5...0.4.6)

---
updated-dependencies:
- dependency-name: dvclive[image]
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-08 14:32:48 +02:00
dependabot[bot] 33228b5cd4 build(deps): Bump scmrepo from 0.0.3 to 0.0.4
Bumps [scmrepo](https://github.com/iterative/scmrepo) from 0.0.3 to 0.0.4.
- [Release notes](https://github.com/iterative/scmrepo/releases)
- [Commits](https://github.com/iterative/scmrepo/compare/0.0.3...0.0.4)

---
updated-dependencies:
- dependency-name: scmrepo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-08 12:01:56 +01:00
Ruslan Kuprieiev 064fbfd0d5 webdav: fix unstrip_protocol 2021-12-07 23:59:12 +02:00
Ruslan Kuprieiev e8e1c76504 fs: path: use ntpath/posixpath
This is less error-prone than implementing these methods ourselves.
2021-12-07 22:58:48 +02:00
Saugat Pachhai (सौगात) 532e115782 exp init: unhide from dvc exp --help 2021-12-07 09:55:40 +05:45
Saugat Pachhai 189f4a0216 Update dvc/repo/experiments/init.py 2021-12-07 09:55:03 +05:45
pre-commit-ci[bot] 0bc101964e [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-12-07 09:55:03 +05:45
Saugat Pachhai (सौगात) 3319808ed2 exp init: use shortlinks for doclinks 2021-12-07 09:55:03 +05:45
Saugat Pachhai (सौगात) cd37ebca37 exp init: minor refactoring 2021-12-07 08:40:46 +05:45
dberenbaum 4385720fab exp init: update tests 2021-12-07 07:34:31 +05:45
dberenbaum d52123f850 exp init: change default stage name to train 2021-12-07 07:34:31 +05:45
pre-commit-ci[bot] 9803db6a64 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-12-07 06:17:02 +05:45
pre-commit-ci[bot] bcb1ba0ea8 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/ambv/black: 21.11b1 → 21.12b0](https://github.com/ambv/black/compare/21.11b1...21.12b0)
2021-12-07 06:17:02 +05:45
Saugat Pachhai (सौगात) 389fd48b5a exp init: change type to --type=live to --type=dl 2021-12-07 05:56:23 +05:45
Ruslan Kuprieiev 2ecbb16769 setup: bump scmrepo 2021-12-06 19:27:16 +02:00
Ruslan Kuprieiev ec73300782 odb: make some listing methods private
These are not used anywhere outside of odb, so we can make them private
to reduce the scope for the upcoming refactoring.
2021-12-06 14:10:50 +02:00
Paweł Redzyński 954ac6ab03 plots: roll back templates in packages change
Fixes: #6911
Reverts: a1b146669c
2021-12-06 09:55:13 +02:00
dependabot[bot] 1020b06a16 build(deps-dev): Bump pylint from 2.12.1 to 2.12.2
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.12.1 to 2.12.2.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.12.1...v2.12.2)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-06 09:47:25 +02:00
karajan1001 9cf1f45e71 proc: implement cleanup/removal (#7063)
fix: #7063
Add `cleanup` and `removal` function to ProcessManager
2021-12-06 15:25:15 +09:00
dependabot[bot] b67e75c691 build(deps-dev): Bump requests-kerberos from 0.13.0 to 0.14.0
Bumps [requests-kerberos](https://github.com/requests/requests-kerberos) from 0.13.0 to 0.14.0.
- [Release notes](https://github.com/requests/requests-kerberos/releases)
- [Changelog](https://github.com/requests/requests-kerberos/blob/master/HISTORY.rst)
- [Commits](https://github.com/requests/requests-kerberos/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: requests-kerberos
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-12-06 11:38:43 +08:00
David de la Iglesia Castro 728731d4d7 ui: Add open_browser.
Use `webbrowser.open` and relpath for WSL plattform.
2021-12-05 15:44:13 +02:00
Paweł Redzyński 6723a34bda render: image: make html less ugly 2021-12-05 15:42:07 +02:00
karajan1001 eb26b9a78d ci: fix ci failure
We didn't mock `os.kill` in `test_dead_process` and the pid it
kills might coincidentlly be the same with some real process.

1. Add a mocker to mock out `os.kill` in `test_dead_process`.
2. Use reset_mock to replace setting it again.
2021-12-04 19:40:57 +09:00
Ruslan Kuprieiev 42641ddd20 gha: remove remote leftovers
Followup for #7087
2021-12-03 23:11:22 +02:00
Ruslan Kuprieiev b6f2a8e653 tests: remove redundant remote tests
With introduction of https://github.com/iterative/dvc-plugins,
all important tests are being ran there, so there is no need
for dvc repo to run them again.

This PR also removes some redundant/broken remote tests.
2021-12-03 22:45:41 +02:00
Ruslan Kuprieiev 024f06d672 gdrive: fix path operations 2021-12-02 18:52:18 +02:00
Ruslan Kuprieiev 8dd3b28729 gdrive: fix typo 2021-12-02 17:22:47 +02:00
Ruslan Kuprieiev d4178c080b gdrive: don't forget bucket when generating a url 2021-12-02 17:21:51 +02:00
Ruslan Kuprieiev 302f5b07f6 gdrive: fix typo 2021-12-02 17:19:12 +02:00
karajan1001andPeter Rowlands 7d68863ba2 proc: implement process signaling(#7062)
fix: #7062
1. Add `send_signal`, `kill`, `terminate` to processManager.
2. Add tests for each of them.

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2021-12-02 16:39:41 +09:00
Ruslan Kuprieiev 09f48a5aaa tests: introduce dvc.testing
This is a very early version of test utils for dvc plugins. This module
consists of existing tests with minimal changes and is going to evolve
a lot as our plugins are getting filled in and pytest-test-utils matures.
2021-12-02 09:08:40 +02:00
dependabot[bot] 004e15c2c0 build(deps-dev): Bump types-requests from 2.26.0 to 2.26.1
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.0 to 2.26.1.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-29 20:24:26 +05:45
dependabot[bot] 382edfc382 build(deps-dev): Bump dvclive[image] from 0.4.3 to 0.4.5
Bumps [dvclive[image]](https://github.com/iterative/dvclive) from 0.4.3 to 0.4.5.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.4.3...0.4.5)

---
updated-dependencies:
- dependency-name: dvclive[image]
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-29 17:17:46 +05:45
Peter Rowlands dd535f617b proc: initial ProcessManager skeleton 2021-11-29 07:00:38 +02:00
Peter Rowlands 15eb326188 proc: initial ManagedProcess implementation
- supports running (backgrounded) processes with redirected output
2021-11-29 07:00:38 +02:00
dependabot[bot] 34f402659c build(deps): Bump scmrepo from 0.0.1 to 0.0.2
Bumps [scmrepo](https://github.com/iterative/scmrepo) from 0.0.1 to 0.0.2.
- [Release notes](https://github.com/iterative/scmrepo/releases)
- [Commits](https://github.com/iterative/scmrepo/compare/0.0.1...0.0.2)

---
updated-dependencies:
- dependency-name: scmrepo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-26 09:36:57 +05:45
dependabot[bot] 3e808b6f37 build(deps-dev): Bump pylint from 2.11.1 to 2.12.1
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.11.1 to 2.12.1.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.11.1...v2.12.1)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-26 09:36:25 +05:45
Saugat Pachhai (सौगात) ea52ab2260 remove dvc.scm and dvc.fs.git module; rename dvc.fs.scm to dvc.fs.git 2021-11-25 19:09:58 +02:00
Paweł Redzyński 57c6b1036f gha: allow builds for new 3.9 versions
Closes: #6975
2021-11-25 14:40:40 +02:00
Saugat Pachhai 950b576cbd packages: build binary pkgs with Python 3.9 2021-11-25 14:40:11 +02:00
Saugat Pachhai (सौगात) 2cbe25ac90 scm: move warning on unsupported index to scmcontext
Also makes `Repo.init` avoid using `scm.add` directly.
2021-11-25 12:29:50 +02:00
Saugat Pachhai (सौगात) c9cd4a8c51 scm: remove dvc specific message; add client kwarg
This commit removes dvc specific message hardcoded in
scm.detach_head. Now that we are decoupling scm, we are
adding `client=` kwarg to be set by the caller.
2021-11-25 12:05:25 +02:00
Saugat Pachhai (सौगात) 95c726b297 fs.git: refactor GitFileSystem to be fsspec compliant
Also adds a wrapper for DVC to use as GitFileSystem in dvc.fs.scm.
This is temporary, and will be renamed back to dvc.fs.git after the
split.

Note that this filesystem is not fully compliant, it's only
meant to be compatible with our previous implementation.
2021-11-25 08:32:20 +05:45
Saugat Pachhai (सौगात) 281b97c8ef scm: remove dependency from dvc's utils in scm
This removes dependency from dvc.utils and dvc.types from DVC.
Some utils like fix_env, is_binary have been reimplemented.
Regarding fix_env and is_binary, I think eventually this
should be a responsibility of DVC itself to set.

relpath implementation in DVC also works with paths in
different drives in Windows. I have changed this to use
os.path.relpath for now.
2021-11-24 17:00:12 +02:00
Saugat Pachhai (सौगात) a282e0e48c scm: restructure scm
Makes it easier to decouple, we'd only need to rename
dvc/scm/__init__.py to dvc/scm.py.
2021-11-24 11:46:40 +05:45
Saugat Pachhai (सौगात) 48581715b6 scm: remove exp related behaviours from scm.resolve_rev 2021-11-24 11:39:39 +05:45
dependabot[bot] 13ca959e98 build(deps): Bump actions/cache from 2.1.6 to 2.1.7
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.6 to 2.1.7.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.6...v2.1.7)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-24 08:47:13 +05:45
Saugat Pachhai (सौगात) a0f4e12fb4 scm: unified progress reporting in a structured way
This adds a support for reporting progress from Git in a structured
format. The gitpython already provides somewhat structured progress
reporting, dulwich on the other hand only provides git-like line
messages.

As gitpython uses git binary, it already parse this line format
in a structured way. This PR makes dulwich use the same machinery
to unify progress reporting for both dulwich and gitpython python.

This does add a dependency on gitpython even on dulwich backend,
so I am iffy if we should do this.
The alternative is to translate gitpython's progress reports to
string, and just use string messages as progress reports on both
dulwich and gitpython.
2021-11-23 19:50:12 +02:00
Saugat Pachhai (सौगात) d95dd27773 scm: map internal SCMError to DVC's exception type 2021-11-23 19:21:51 +02:00
Saugat Pachhai (सौगात) f76ef84c88 exceptions: migrate other scm exceptions to internal 2021-11-23 19:21:51 +02:00
Saugat Pachhai (सौगात) f2f4227656 exceptions: migrate RevError 2021-11-23 19:21:51 +02:00
Saugat Pachhai (सौगात) 7ca8fbbc82 exceptions: migrate CloneError 2021-11-23 19:21:51 +02:00
Saugat Pachhai (सौगात) 8ec1ff44de exceptions: move AuthError and InvalidRemote errors 2021-11-23 19:21:51 +02:00
Saugat Pachhai (सौगात) c19a11152a scm: move FileNotInRepoError and MergeConflictError to scm.exceptions 2021-11-23 19:21:51 +02:00
Saugat Pachhai (सौगात) cab60b54f8 live: import webbrowser lazily; only patch webbrowser.open 2021-11-23 22:08:21 +05:45
David de la Iglesia Castro fc7caa487f tests: Patch webbrowser.open on session level
Closes #7014
2021-11-23 22:08:21 +05:45
Saugat Pachhai (सौगात) 568abb3e28 scm: remove dependency from DvcException 2021-11-23 13:19:41 +02:00
Kwon-Young ChoiandPeter Rowlands 3e7cff3e35 exp: respect repo hardlink_lock config for the exp scm lock
- Will fix #7029

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2021-11-23 19:51:42 +09:00
Saugat Pachhai (सौगात) 7dc5420548 metrics/params diff: catch NoSCMError instead of a broad exception
instead of catching broad exception, NoSCMError should work here.
2021-11-23 13:21:15 +05:45
pre-commit-ci[bot] 644e9ede92 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/ambv/black: 21.10b0 → 21.11b1](https://github.com/ambv/black/compare/21.10b0...21.11b1)
2021-11-22 21:36:19 +02:00
Paweł Redzyński a9512e8585 plots: collect: return callable instead of data
Related to: #6923, iterative/viewer#2657
2021-11-22 21:31:20 +02:00
Saugat Pachhai (सौगात) 00e53c5c32 tests: fix black 2021-11-22 17:33:12 +05:45
Saugat Pachhai (सौगात) 0aaf96bce4 exp init: show 'n to omit' on --explicit mode 2021-11-22 17:20:16 +05:45
Saugat Pachhai (सौगात) e07b99ed33 tests: increase timeout to 2 second for prompts test 2021-11-22 15:10:55 +05:45
Saugat Pachhai (सौगात) 7bc906011f tests: timeout only on functions, excluding tests fixtures
Sometimes, `tmp_dir` fixtures take longer than a second. Using `func_only`
makes it only consider duration of test function run, excluding fixtures
duration.
2021-11-22 15:10:55 +05:45
Saugat Pachhai (सौगात) b84227db89 scm: remove ignore list 2021-11-22 07:16:54 +02:00
David de la Iglesia Castro 318b269ad1 live: Call webbrowser.open.
Use `funcy.once_per_args` decorator to prevent opening the html multiple times if user closes first time.

Closes iterative/dvclive#180
2021-11-22 03:35:23 +02:00
Ruslan Kuprieiev 1176b6d875 Update setup.cfg 2021-11-22 03:31:23 +02:00
dependabot[bot] ee6539292b build(deps): Bump adlfs from 2021.9.1 to 2021.10.0
Bumps [adlfs](https://github.com/dask/adlfs) from 2021.9.1 to 2021.10.0.
- [Release notes](https://github.com/dask/adlfs/releases)
- [Changelog](https://github.com/dask/adlfs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dask/adlfs/compare/2021.09.1...2021.10.0)

---
updated-dependencies:
- dependency-name: adlfs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-22 03:31:23 +02:00
Saugat Pachhai (सौगात) bf313d92aa exp init: timeout prompts' test
The `exp init` tests may just keep waiting on the prompts blocking tests.
The blocking of prompts is likely a failure in tests setup or a bug
in the implementation. It's hard to find out these failures or avoid it.
If they take longer than 1 second, they most likely have failed as
most of test takes 0.1 second at max.
2021-11-22 03:27:16 +02:00
Saugat Pachhai (सौगात) 46289182d2 tests: run on Windows with Python 3.10
pygit2 wheels should be now available.
Had to skip pytest-docker for Windows + Python 3.10 due to
a dependency issue with it's dependency, see:
https://github.com/docker/docker-py/issues/2902.
2021-11-20 18:52:31 +02:00
Saugat Pachhai (सौगात) cb941a836d tests: cover exp init 2021-11-19 18:58:23 +05:45
Gustavo José de Sousa a70b3087b4 build: bump sshfs from 2021.8.1 to 2021.11.2
This version uses bumps asyncssh to a version that supports ProxyJump.
2021-11-18 23:58:57 +02:00
Ruslan Kuprieiev c9dd137426 fs: BaseFileSystem -> FileSystem
Following fsspec conventions and new object conventions.
2021-11-18 19:58:23 +02:00
Saugat Pachhai (सौगात) 93e6526907 scm: split tracking of to-be git-ignored/git-staged into SCMContext 2021-11-18 15:34:45 +02:00
dependabot[bot] 230eb7087d build: bump google-cloud-storage from 1.42.3 to 1.43.0
Bumps [google-cloud-storage](https://github.com/googleapis/python-storage) from 1.42.3 to 1.43.0.
- [Release notes](https://github.com/googleapis/python-storage/releases)
- [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-storage/compare/v1.42.3...v1.43.0)

---
updated-dependencies:
- dependency-name: google-cloud-storage
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-18 16:36:00 +05:45
Saugat Pachhai (सौगात) c3b91e5f85 exp init: interactive mode refactor and simplification 2021-11-18 16:06:52 +05:45
Saugat Pachhai (सौगात) eac030598c ui.prompt: move prompts from exp init to ui.prompt
Some fixes along the way to make it more generic, but it still has
lots of features tied with exp init.
2021-11-18 13:22:45 +05:45
karajan1001 068b8371aa Machine: table output for machine status(#6892)
Change the output format of `dvc machine status` command to tables for a
better readability.
2021-11-18 11:22:13 +08:00
karajan1001 888b6392bb Machine:remove field name in config(#6892)
Because our machine name now is the key value of the machine config
dict. And the machine config value will been overwritten by it when the
config is loaded. We can remove the field `name` from the config.
2021-11-18 11:22:13 +08:00
karajan1001 b24a31b834 Machine: use table for list outputs(#6892)
fix: #6892

Current output of machine list is quite urgly, it is originaly used to
show configurations. In this PR, we use a new table format for it.
2021-11-18 11:22:13 +08:00
karajan1001 45553c5b30 TabularData: add a new how arg to dropna(#6892)
Borrowed this idea from pandas api. Current dropna methods will drop a
column or row if any data is missing. But it is also quite useful to
drop those columns or rows only if all of its elements are missing. This
is mostly used for pruning a table.
2021-11-18 11:22:13 +08:00
Peter Rowlands 28f57933db dulwich: flush stdin channel on asyncssh vendor write 2021-11-17 12:48:34 +02:00
Peter Rowlands 25dca0b665 dulwich: workaround github SSH server in asyncssh vendor
- allow mismatched RSA signature algo names in initial
  SSH_MSG_USERAUTH_PK_OK response
- unit test overridden asyncssh publickey auth compatibility with
  github ssh server
2021-11-17 12:48:34 +02:00
Peter Rowlands 45c4e1998d dulwich: fallback to default client config in asyncssh vendor
- vendor should use ssh-agent or default IdentityFile(s) when no SSH key
  has been provided by the user
- encrypted (fallback) keys will always be passed to ssh-agent
2021-11-17 12:48:34 +02:00
karajan1001 eea67a6396 machine:remove help text for irrelevant commands (#6940)
fix: #6940

`--global/system/project/local` help message are in some irrelevant
command `status`, `ssh`, `create`, `destroy`. These commands are more
related to running instances rather than `machine config`, level
options didn't work for them.

1. Remove `--global/system/project/local` options in `status`, `ssh`,
   `create`, `destroy`.
2. Add a test for it.
3. Correct a typo error.
2021-11-17 16:25:48 +08:00
karajan1001andDave Berenbaum f43c04f935 Make machine status name optional
fix: #6838
1. make machine status name optional.
2. show all machine status if no name provide.
3. UI changes for this cmd.
4. add a new test for it.

Co-authored-by: Dave Berenbaum <[email protected]>
2021-11-17 15:44:36 +08:00
dependabot[bot] 3486435a7f build: bump filelock from 3.3.2 to 3.4.0
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.3.2 to 3.4.0.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.3.2...3.4.0)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-17 14:04:17 +08:00
Paweł Redzyński e0fd0790fc plots: special chars handling for vega plots paths
Fixes: #6894
2021-11-17 01:57:35 +02:00
dberenbaum f99d3cda57 scm: hint to autostage 2021-11-16 18:01:18 +02:00
pre-commit-ci[bot] 003461f9a2 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/timothycrosley/isort: 5.9.3 → 5.10.1](https://github.com/timothycrosley/isort/compare/5.9.3...5.10.1)
2021-11-16 07:55:56 +05:45
Ruslan Kuprieiev 88660f6a66 dvc: use transfer in checkout/doctor/odb
All three of those use more or less the same logic for testing different
link types and maybe then using them. So now instead of having 3
implementations, we'll have just one.

Filesystem `transfer` now will not try to catch particular errno's, but
instead will just ignore any OSError and move down to the next link type
to try out, and if the last one fails - it will raise the last exception,
so if it is a common problem - we'll be able to see it.

This patch also removes `odb.confirmed_cache_type`, as it was caching
link type that is not really a cache-level thing, but rather a checkout thing,
that should only be cached for particular objects we are linking, as multiple
objects could be linked to different filesystems from the same odb (e.g.
external local output on hdd and local data on your ssd).

Next step here will be to introduce metadata diff, so that we can get rid of
temporary Link wrapper and introduce Transfer and corresponding transfer
config, that will know what links to use and could have some other things
like callbacks, so we don't continue passing dozens of kwargs along the stack.

Continuing from #6963

Fixes #6980
2021-11-15 22:50:00 +02:00
David de la Iglesia Castro 7df6090a9c params: Use ast.literal_eval for python params.
Add unit tests for `parse_py`

Closes  #6953
2021-11-15 17:52:49 +02:00
Dimitar Todorov fdba79c519 change method override to call super instead of repeating the calls 2021-11-15 17:50:14 +02:00
Димитър Тодоров (Dimitar Todorov)andSaugat Pachhai 2f6b6cc586 Update dvc/fs/http.py
Co-authored-by: Saugat Pachhai <[email protected]>
2021-11-15 17:50:14 +02:00
Dimitar Todorov 0fea622266 push: fix http storage ignoring method setting
Add method overriding the default put_file in http.py
where the upload method is passed to the http implementation.

Fixes: #6694
2021-11-15 17:50:14 +02:00
David de la Iglesia Castro 4a1985d7cd dvc render: Add RENDERERS registry.
Related: #6840, #4455, #6943
2021-11-15 16:37:24 +01:00
David de la Iglesia Castro c8903dc3a6 live: Add test for logging images with dvclive
Closes iterative/dvclive#190
2021-11-15 12:47:02 +01:00
David de la Iglesia Castro 28dd39a1a0 exp diff: Remove --old option.
After #6814 the header of `diff_table` is `a_rev/`b_rev` instead of `Old`/`New` so the name of the option was counterintuitive.

In addition, make `a_rev` show by default, for consistency with output format of other `diff_table` uses (`dvc metrics diff` / `dvc params diff`).
2021-11-12 12:58:12 +02:00
dependabot[bot] ef9d8580cc build: bump types-requests from 2.25.12 to 2.26.0
Bumps [types-requests](https://github.com/python/typeshed) from 2.25.12 to 2.26.0.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-12 12:40:38 +02:00
dependabot[bot] 1af23f32b7 build: bump pyinstaller from 4.6 to 4.7
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 4.6 to 4.7.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/v4.7/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v4.6...v4.7)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-12 12:40:27 +02:00
Saugat Pachhai (सौगात) 7b488887b4 exp init: suppress plots if live is selected as a stage type 2021-11-12 11:25:39 +05:45
Saugat Pachhai (सौगात) 8262dbd37c exp init: hide metrics/plots from worktree on --type=live 2021-11-12 11:21:58 +05:45
Ruslan Kuprieiev 264d78b637 ostage: don't forget to convert https etags
Fixes #6966
2021-11-12 00:54:44 +02:00
Ruslan Kuprieiev 1b2400842d checkout: catch ENOSYS 2021-11-11 22:38:29 +02:00
Saugat Pachhai (सौगात) 81db879c51 hardlink: fallback to other link methods if 38 error code is received
Fixes #6962, where GDdrive mounted on Colab was failing when trying to hardlink
with the message that the 'Function not implemented'.
2021-11-11 22:38:29 +02:00
Saugat Pachhai (सौगात) d668cbeb45 exp init: warn for code/data if they don't exist 2021-11-11 08:09:37 +05:45
Saugat Pachhai (सौगात) a466241552 exp init: validate params as soon as it's entered in prompt
It'll give a error message to the user and reprompt them.
This mechanism can be extended to other prompts, and we
can show a warning message and/or reprompt them based on
validations.

Closes #6865. Part of #6446.
2021-11-11 08:09:37 +05:45
Ruslan Kuprieiev 7e3e257c7f utils: fs: remove unused fs_copy 2021-11-11 01:26:37 +02:00
Paweł Redzyński ab56e4bb9b gha: limit python version to 3.9.7
Related: https://bugs.python.org/issue45235
Closes: #6956
2021-11-11 01:14:03 +02:00
Paweł Redzyński 36ed182f26 argparse: dont override cmd
Related: #6956, https://bugs.python.org/issue45235
2021-11-11 01:14:03 +02:00
Ruslan Kuprieiev 045d1f5e3e localfs: remove LocalPath
fs.path is meant to be lightweight, and we should try to avoid using os.path.

Leftover from #6946
2021-11-10 20:10:48 +02:00
Ruslan Kuprieiev 5214aec1bb fspath: introduce relparts 2021-11-10 20:10:48 +02:00
David de la Iglesia Castro 72066f233b exp show: Add --only-changed option.
Use TabularData `drop_duplicates` to only show metrics/params with values varying across the selected experiments.

Fixes #5966
2021-11-10 16:52:18 +02:00
David de la Iglesia Castro cb6f625dd9 TabularData: Add drop_duplicates method.
This method mimics pandas `drop_duplicates` when axis=rows.

For axis=cols this method will drop columns with 0-variance (only
1 unique value). NaNs (`_fill_value`) won't be counted as unique values.

So the following column (`-` would be the `_fill_value`) will be dropped:

foo
-
foo
foo
2021-11-10 16:52:18 +02:00
dependabot[bot] 87fadd3225 build: bump dvclive from 0.4.2 to 0.4.3
Bumps [dvclive](https://github.com/iterative/dvclive) from 0.4.2 to 0.4.3.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.4.2...0.4.3)

---
updated-dependencies:
- dependency-name: dvclive
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-10 11:46:41 +01:00
dependabot[bot] 8f028028b9 build: bump types-requests from 2.25.11 to 2.25.12
Bumps [types-requests](https://github.com/python/typeshed) from 2.25.11 to 2.25.12.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-10 11:45:42 +01:00
Batuhan Taskaya 9aec8f1926 dvc: get rid of path_info
Intermediate PR in removing path_info from our code base.

It is completely removed from the main codebase and is still
being used in tests, which will be migrated in the followups.

For fs-specific simple path manipulation, we've introduced
`fs.path` helper (similar to `os.path`).

Fixes #6774
2021-11-10 00:52:48 +02:00
dependabot[bot] 4175f9ffa4 build: bump s3/gcsfs from 2021.10.1 to 2021.11.0
Bumps [gcsfs](https://github.com/fsspec/gcsfs) from 2021.10.1 to 2021.11.0.
- [Release notes](https://github.com/fsspec/gcsfs/releases)
Bumps [s3fs](https://github.com/fsspec/s3fs) from 2021.10.1 to 2021.11.0.
- [Release notes](https://github.com/fsspec/s3fs/releases)
- [Changelog](https://github.com/fsspec/s3fs/blob/main/release-procedure.md)

---
updated-dependencies:
- dependency-name: s3fs
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: gcsfs
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-08 11:13:52 +08:00
Guðmundur HeimissonandRuslan Kuprieiev c5b1a73dc5 webhdfs: expose kerberos and https options (#6936)
* Add fsspec options for webhdfs

* Add requests-kerberos dependency

* Update dvc/fs/webhdfs.py

Simplify config.pop.

Co-authored-by: Ruslan Kuprieiev <[email protected]>

* Update dvc/fs/webhdfs.py

* Simplify kerb_kwargs config setting.

Co-authored-by: Ruslan Kuprieiev <[email protected]>

Co-authored-by: Ruslan Kuprieiev <[email protected]>
2021-11-07 20:42:10 +02:00
Saugat Pachhai (सौगात) 044e79000d deps: bump rich to >=10.13.0
It comes with the support for no. of parameters that matches with json.dumps.
See: https://github.com/willmcgugan/rich/pull/1547 and
https://github.com/willmcgugan/rich/pull/1644.

Also added support for printing json to stderr. Before we used to use json.dumps
and rich.json.JSON based on isatty. Now we use rich.print_json on both
tty and non-tty conditions, which may avoid different behaviours in the
future.
2021-11-07 19:38:06 +02:00
karajan1001 be6128470f machine status: include two new field in the output
1. include instance_type and instance_gpu in the output.
2. rerange the field by importance.
2021-11-04 15:10:45 +02:00
Saugat Pachhai (सौगात) dd0d4f5a2c exp init: don't try tracking params if they are skipped 2021-11-03 19:21:10 +05:45
Saugat Pachhai (सौगात) 6e0dfb94e4 exp init: hide message about workspace tree on --explicit 2021-11-03 18:59:25 +05:45
Saugat Pachhai (सौगात) ce6c8a5678 exp init: fix --explicit failing with KeyError
Fixes #6917
2021-11-03 16:42:45 +05:45
dependabot[bot] b927337a30 build(deps): bump actions/checkout from 2.3.5 to 2.4.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.5 to 2.4.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.3.5...v2.4.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-03 12:47:22 +02:00
Saugat Pachhai (सौगात) 03b372a073 exp init: ask for command before paths 2021-11-03 05:23:40 +05:45
Saugat Pachhai (सौगात) 915a755e2f build: remove numpy restriction 2021-11-03 05:23:17 +05:45
Paweł Redzyński 22cc2a5cbc plots: fix resolve template path bug
Fix: #6854
2021-11-02 16:49:56 +02:00
Saugat Pachhai (सौगात) a67d8a44b3 exp init: make confirmation prompt at the end similar to others 2021-11-02 17:24:09 +05:45
Saugat Pachhai (सौगात) 80a52e65ad exp init: minor refactor on way it returns prompted values 2021-11-02 14:57:40 +05:45
Saugat Pachhai (सौगात) 208ccb2430 exp init: autostage .gitignores, dvc.yaml and params file 2021-11-02 12:00:06 +05:45
dependabot[bot] 97e97c7f24 build(deps-dev): bump filelock from 3.3.1 to 3.3.2
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.3.1...3.3.2)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-02 08:16:37 +05:45
dependabot[bot] 1d1451ddd3 build(deps): bump pyinstaller from 4.5.1 to 4.6
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 4.5.1 to 4.6.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/v4.6/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v4.5.1...v4.6)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2021-11-02 08:16:18 +05:45
pre-commit-ci[bot] 0cc7c4c02b [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/ambv/black: 21.9b0 → 21.10b0](https://github.com/ambv/black/compare/21.9b0...21.10b0)
2021-11-02 06:30:13 +05:45
Saugat Pachhai d3d459caa0 Revert "deps: require chardet as a dependency"
This reverts commit a4dc8ddc74.
2021-11-02 06:17:31 +05:45
Saugat Pachhai a4dc8ddc74 deps: require chardet as a dependency
Looks like azure-core is dependent on a dependency `chardet` that aiohttp brought it with, which has been replaced with `charset_normalizer`.
But azure-core depends on internal of aiohttp and looks like it tries to set it's own encoding parser, `chardet` which is now not available.
2021-11-01 18:25:39 +02:00
Saugat Pachhai 3eceeea3e8 Remove python 3.10 restriction for pyarrow
Pyarrow 6 is now available for Python 3.10. Fixes https://github.com/iterative/dvc/issues/6878.
2021-11-01 20:00:21 +05:45
Saugat Pachhai (सौगात) 8fc959060a tests: skip based on conditional import on pyarrow
From now on, pyarrow dependency is optional for the tests.
2021-11-01 19:15:28 +05:45
David de la Iglesia Castro 0638d99fe3 LiveTask: write output path at start/end
Writing at the end of each step (create_summary) can mess up with some ML Framework progress bars, I moved the message to the monitor task, as the path won't change during training.

Fixes iterative/dvclive#177
2021-10-29 14:41:03 +02:00
David de la Iglesia Castro 24abc6f328 TabularData: Add dropna method.
This method mimics pandas `dropna`.
2021-10-29 15:28:36 +03:00
David de la Iglesia Castro 4dd544121f plots: Use URI for --open.
We were previously using relpath in *all* cases, mainly to support WSL but relpath doesn't work
on macOS.

Added a check for using relpath *only* for WSL.

Fixes #6092
2021-10-29 13:27:12 +02:00
Ruslan Kuprieiev 869ea442ef output: save: remove redundant changed() call
No longer needed since object introduction, as we will compare staged
object during commit/checkout anyway.
2021-10-29 01:45:10 +03:00
Ruslan Kuprieiev 922eb20e44 staging: don't use move 2021-10-27 20:19:17 +03:00
Ruslan Kuprieiev a2f91ed5fd checkout: always confirm link type
Currently whether or not link confirmation will run depends on
particular filesystem and existing workspace link type, which introduces
inconsistency in behaviour, where we sometimes catch unsupported links
before touching user data and sometimes we don't.
2021-10-27 20:19:17 +03:00
Ruslan Kuprieiev 017762fc2c checkout: don't forget to protect() back objects
Depending on a filesyste (e.g. on NTFS), `_remove` might reset
read-only permissions in order to delete a hardlink to protected object,
which will also reset it for the object itself, making it unprotected,
so we need to protect it back.
2021-10-27 20:19:17 +03:00
Ruslan Kuprieiev 874aceccf8 system: use linkat instead of link
`os.link` is broken across platforms when dealing with symlinks.

See https://bugs.python.org/issue41355 for more info.
2021-10-27 20:19:17 +03:00
Ruslan Kuprieiev 41aee9a3b7 localfs: use mtime/size/inode for checksum
Currently it depends on the whole `info()` output, which has too much
information that doesn't really matter. To check if file contents didn't
change mtime, size, inode combo is enough.
2021-10-27 20:19:17 +03:00
Peter Rowlands (변기호) 0d895e2661 exp: add initial ssh executor (#6833)
* executor: move local specific git initialization out of base

* machine: return kwargs for executor init instead of executor object

* executor: add initial ssh executor

* ssh-executor: disable strict host checking for dvc-machine instances

* initial tests for ssh executor

* drop GIT_SSH_COMMAND usage

* push: accept destination odb as a parameter

* separate cache initialization into executor.init_cache

* add test for ssh executor cache init

* remove dead code

* machine: fixture-ize default test scenarios

* test SSHExecutor.from_machine

* tpi: return default SSH port for constructing executors
2021-10-26 16:08:17 +09:00
Saugat Pachhai 14eaba4cdb exp init: iterate on ui, tree and show to-be-generated yaml content (#6750)
* iterate on ui, has tree and shows to-be-generated yaml content

* order fs tree structure alphabetically

* always show the heading

* hide dvclive on tree if not on live mode

* Change message at the end

* s/skip/omit

* add link on the header

* remove newline on non-interactive mode
2021-10-26 08:22:33 +05:45
Dave Berenbaum 3064738a34 fix msg in for ExperimentExistsError (#6864) 2021-10-26 10:55:08 +09:00
GaoandPeter Rowlands c07592131a exp run: add experiment name check. (#6848)
* Add experiment name check.

1. Add experiment name check (https://git-scm.com/docs/git-check-ref-format)
2. Add duplicate exp name check.
3. Add some unit test for it.

* Ban slash / in dvc exp names

* Use dulwich backend for ref name checking

* Some bug fix

* Update dvc/repo/experiments/__init__.py

Co-authored-by: Peter Rowlands (변기호) <[email protected]>

* Update dvc/repo/experiments/__init__.py

Co-authored-by: Peter Rowlands (변기호) <[email protected]>

* Some review changes

* Make some funtion more reusable.

Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2021-10-25 20:16:37 +09:00
Saugat Pachhai 52a9cddc14 exp init: remove default in cli, add default in help instead (#6862)
The default made it harder to detect between the default and explicitly provided values, which we need for interactive mode.
2021-10-25 16:32:17 +05:45
Ruslan Kuprieiev 38cbebfb28 fs: transfer: try reflink (#6853)
If supported, reflink, will provide the fastest transfer possible.
That being said, right now we use `move` in most of the places,
which still takes presidence.

Pre-requisite for #6387
2021-10-24 22:46:02 +03:00
dependabot[bot]andSaugat Pachhai 5017c2f282 build(deps): update pyparsing requirement from ==2.4.7 to >=2.4.7
build(deps): update pyparsing requirement from ==2.4.7 to >=2.4.7 

Updates the requirements on [pyparsing](https://github.com/pyparsing/pyparsing) to permit the latest version.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_2.4.7...pyparsing_3.0.0)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Saugat Pachhai <[email protected]>
2021-10-24 15:22:46 +00:00
dependabot[bot] 1bf6cc7c2f build(deps): bump pylint-pytest from 1.0.3 to 1.1.2 (#5894)
Bumps [pylint-pytest](https://github.com/reverbc/pylint-pytest) from 1.0.3 to 1.1.2.
- [Release notes](https://github.com/reverbc/pylint-pytest/releases)
- [Changelog](https://github.com/reverbc/pylint-pytest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/reverbc/pylint-pytest/compare/pylint-pytest-1.0.3...pylint-pytest-1.1.2)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-24 20:08:58 +05:45
dependabot[bot] 27666ffba7 build(deps-dev): Bump dvclive from 0.4.1 to 0.4.2 (#6846)
Bumps [dvclive](https://github.com/iterative/dvclive) from 0.4.1 to 0.4.2.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.4.1...0.4.2)

---
updated-dependencies:
- dependency-name: dvclive
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-24 19:03:24 +05:45
Ruslan Kuprieiev e2d705fd76 link: use proper exceptions (#6852)
* link: use proper exceptions

* system: use ENOTSUP instead of NotImplementedErrorwq
2021-10-23 02:38:17 +03:00
Saugat Pachhai 719e93192f move scm.install to dvc.install (#6849)
scm.install does 3 different things, and all of those are
very much tied to DVC. So, as we are splitting scm out,
it should now belong to `dvc.install`.
2021-10-22 14:53:33 +03:00
Saugat Pachhai 501256708d use system hook for mypy; fix raised issues (#6851) 2021-10-22 15:40:54 +05:45
Saugat Pachhai 800fbc7ad0 Use -n=auto in tests
Linux/Windows machines have only 2 cores, whereas macOS machines have 3 cores. Maybe we are stretching available resources by 2x, let's see if `-n=auto` improves test freezes.
2021-10-22 08:46:55 +05:45
Peter Rowlands (변기호) 73f2fa8c93 git: use asyncssh as dulwich SSH vendor (#6797)
* git: support client kwargs for auth in remote methods

* tests: use docker for git/ssh server

* move dulwich backend into subdir

* add base sync<->async support

* dulwich: add initial asyncssh SSH vendor

* dulwich: use asyncssh as the default SSH vendor

* add test for git over ssh

* test read (fetch) over ssh

* ci: re-enable ssh tests

temporary workaround for #6788, #6789

* move async stubs into dvc.scm.asyn

* tests: use sshfs and not dvc.fs.ssh

* allow explicit 0-length read in asyncssh vendor
2021-10-21 14:36:45 +09:00
b652670cbf Dependabot/pip/pylint 2.11.1 (#6843)
* build(deps): Bump pylint from 2.9.6 to 2.11.1

Bumps [pylint](https://github.com/PyCQA/pylint) from 2.9.6 to 2.11.1.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.9.6...v2.11.1)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

* fix pylint problems

* Updation after rebase.

* system: use binary mode

* Set remote execution script encoding type locale

* Update dvc/machine/__init__.py

Co-authored-by: Peter Rowlands (변기호) <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ruslan Kuprieiev <[email protected]>
Co-authored-by: Peter Rowlands (변기호) <[email protected]>
2021-10-20 14:34:10 +03:00
Gao 6de3e734ed Revert "build(deps): Bump pylint from 2.9.6 to 2.11.1 (#6636)" (#6842)
This reverts commit 464620806c.
2021-10-20 12:41:00 +03:00
Gao 4b2bec89ec dvc machine: add a new command rename to it (#6633)
* `dvc machine`: add a new command `rename` to it

fix #6481
1. add new command `dvc machine rename`
2. add a unit test for the call.
3. add several functional tests for it.t push myself fix6481

* Move rename logic to tpi

* use tpi.state_mv instead of rename

* Rebase to a newer version

* Update tpi version

* Remove unused assert and try catch
2021-10-20 15:57:44 +09:00
Saugat Pachhai 6c0bc9563c enable pywin32 for Python3.10, make it an optional dependency (#6841) 2021-10-20 11:25:00 +05:45
f964a44e7c Exp show md (#6799)
* Added `--md`/`--show-md` option to `exp show`.

* Added test_show_experiments_md

* Fixed re-defining all_experiments

* Fixed all_experiments

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added dest markdown

* Added textwrap.dedent

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update experiments.py

* Apply suggestions from code review

Co-authored-by: Saugat Pachhai <[email protected]>

* Update tests/unit/command/test_compat_flag.py

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Saugat Pachhai <[email protected]>
2021-10-20 11:04:57 +05:45
464620806c build(deps): Bump pylint from 2.9.6 to 2.11.1 (#6636)
* build(deps): Bump pylint from 2.9.6 to 2.11.1

Bumps [pylint](https://github.com/PyCQA/pylint) from 2.9.6 to 2.11.1.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.9.6...v2.11.1)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

* fix pylint problems
* system: use binary mode

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: karajan1001 <[email protected]>
Co-authored-by: Ruslan Kuprieiev <[email protected]>
2021-10-20 10:32:44 +08:00
Saugat Pachhai 4244e5a475 fix #6835 (#6837) 2021-10-19 21:59:09 +03:00
Saugat Pachhai 0c41a25bbe move some configs to pyproject.toml (#6834) 2021-10-19 19:47:51 +03:00
Matthew Beckers 26f435a433 exp run: throw error if new parameters are added by --set-param (#6521)
* throw error if new parameters are added by dvc run exp -S

Throws an error if dvc run exp --set-param references a parameter key path
that is not in the target config file. This is done to prevent silent errors
in experimentation when new params are accidentally added instead of modifying
current ones due to typos.

This implementation is based on initial suggestions in issue #5477.

* fix mypy confusion from overwriting a var

* tests: remove modify param test that was adding params

One of the test in test_modify_params was still adding a parameter

* tests: remove param modification during exp run in test_untracked

Spurious usage of modifying a parameter when the params.yaml is not yet existing in test_untracked.

* remove benedict dependency for verifying params

Instead, the keypath util module from benedict is adapted into the dvc codebased and used
natively to verify that a keypath exists within a dict.

* exp: list all invalid param modifications in error message

* exp-run: more specific exception for new params via --set-param

Uses established MissingPramsError instead of DvcException. Modified message to be more similar to other uses of this exception.

* Revert "remove benedict dependency for verifying params"

This reverts commit 6170f16dcc4033667fbacb0d0bc13c60898f58c4.

* exp: use internal benedict import for validating params

* exp: combine new param validation into merge_params

Moves new param validation into existing merge_params function as an optional check

* exp: minor ammendments to error message

* Revert "tests: remove param modification during exp run in test_untracked"

This reverts commit c9eb1a62af58586509ded362cc79e75ddc4b119c.

* exp: fix test_untracked to not fail due to new params

Fixed by creating the params file with the new parameter already present.
2021-10-19 17:46:36 +09:00
966dc8a1ca machine: Add a new command dvc machine status (#6649)
* machine: Add a new command `dvc machine status`

fix#6482
1. add a new command `dvc machien status`
2. add a unit test for the cli call.
3. add a unit test for the shown result.

* Update dvc/command/machine.py

Co-authored-by: Saugat Pachhai <[email protected]>

* Update dvc/command/machine.py

Co-authored-by: Saugat Pachhai <[email protected]>

* Update machine.py

* Remove sensitive fields

* Update dvc/machine/__init__.py

Co-authored-by: Casper da Costa-Luis <[email protected]>

* Update tests/func/machine/__init__.py

Co-authored-by: Casper da Costa-Luis <[email protected]>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Use white list to limit shown fields

Co-authored-by: Saugat Pachhai <[email protected]>
Co-authored-by: Casper da Costa-Luis <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2021-10-19 17:38:11 +09:00
eggqq007andZhenyu Gao 6d96ecb376 dvcfs: handle new workspace subdirectories in walk (#6811)
1. add try-catch to catch KeyError exception while walking dvc folder
2. Fix #6695

Co-authored-by: Zhenyu Gao <[email protected]>
2021-10-19 17:33:02 +09:00
Batuhan Taskaya 9e57d1c32d gdrive: remove redundant _gdrive_retry function (#6831) 2021-10-18 21:52:39 +00:00
Batuhan Taskaya c08655ac38 plots: fix the dictionary to return PathInfos (#6830) 2021-10-18 22:04:21 +02:00
David de la Iglesia Castro 4f16852005 exp/metrics/params diff: Replaced Old/New with a_rev/b_rev (#6814)
* Replaced Old/New with a_rev/b_rev

* Fixed tests
2021-10-18 21:09:02 +03:00
Ruslan Kuprieiev 7bef6fd64f setup: bump fsspec deps (#6828) 2021-10-18 21:01:03 +03:00
pre-commit-ci[bot] 422d883e18 [pre-commit.ci] pre-commit autoupdate (#6829)
updates:
- [github.com/lovesegfault/beautysh: v6.1.0 → v6.2.1](https://github.com/lovesegfault/beautysh/compare/v6.1.0...v6.2.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2021-10-18 20:47:48 +03:00
Saugat Pachhai 48004b1340 Revert "webdav: limit httpx to fix the CI" (#6827)
* Revert "webdav: limit httpx to fix the CI (#6800)"

This reverts commit af34f5a0f1.

* Bump webdav4 requirement
2021-10-18 20:32:46 +03:00
dependabot[bot] 2ae421cea2 build(deps-dev): Bump filelock from 3.3.0 to 3.3.1 (#6825)
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.3.0...3.3.1)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-18 19:53:37 +05:45
dependabot[bot] 812fde9891 build(deps-dev): Bump types-requests from 2.25.9 to 2.25.11 (#6820)
Bumps [types-requests](https://github.com/python/typeshed) from 2.25.9 to 2.25.11.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-18 19:51:13 +05:45
dependabot[bot] 4ed4766692 build(deps): Bump actions/checkout from 2.3.4 to 2.3.5 (#6817)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.4 to 2.3.5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.3.4...v2.3.5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-18 19:43:41 +05:45
dependabot[bot] 9943cd43b1 build(deps-dev): Bump types-tabulate from 0.8.2 to 0.8.3 (#6790)
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.2 to 0.8.3.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-tabulate
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-18 19:42:50 +05:45
dependabot[bot] 7d3f35e6ab build(deps-dev): Bump types-toml from 0.10.0 to 0.10.1 (#6821)
Bumps [types-toml](https://github.com/python/typeshed) from 0.10.0 to 0.10.1.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-toml
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-18 20:57:43 +09:00
dependabot[bot] 4de261f717 build(deps-dev): Bump dvclive from 0.4.0 to 0.4.1 (#6786)
Bumps [dvclive](https://github.com/iterative/dvclive) from 0.4.0 to 0.4.1.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.4.0...0.4.1)

---
updated-dependencies:
- dependency-name: dvclive
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-18 13:33:26 +03:00
Paweł Redzyński db75878bef plots: make generator out of collect (#6793) 2021-10-15 17:24:02 +03:00
Ruslan Kuprieiev 7f5c53c10e ui: write_json: don't forget json kwargs (#6810)
Regression from #6743

Fixes #6803
2021-10-15 01:30:22 +03:00
Ruslan Kuprieiev 259d590527 setup.cfg: remove types-paramiko (#6807)
We are no longer using them since migration to sshfs.

Closes #6806
2021-10-14 16:00:10 +03:00
dependabot[bot]andRuslan Kuprieiev 709bfe598f build(deps): Update flufl-lock requirement from <4,>=3.2 to >=5 (#6576)
* build(deps): Update flufl-lock requirement from <4,>=3.2 to >=3.2,<7

Updates the requirements on [flufl-lock](https://gitlab.com/warsaw/flufl.lock) to permit the latest version.
- [Release notes](https://gitlab.com/warsaw/flufl.lock/tags)
- [Commits](https://gitlab.com/warsaw/flufl.lock/compare/3.2...6.0)

---
updated-dependencies:
- dependency-name: flufl-lock
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

* upgrade flufl.lock

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ruslan Kuprieiev <[email protected]>
2021-10-14 15:26:40 +03:00
David de la Iglesia Castro 7e4f443002 Added test_show_experiments_sort_by (#6798)
* Added test_show_experiments_sort_by

* Added comment

* help formatting
2021-10-14 14:49:00 +03:00
2a3a93314b pathspec 0.9.0 quickfix (#6689)
* pathspec 0.9.0 quickfix

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* restrict requirement properly

Co-authored-by: Gao <[email protected]>

* revert change_rule update

* adjust tests to pathspec 0.9.0

* lower bound restrict pathspec

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Gao <[email protected]>
Co-authored-by: Saugat Pachhai (सौगात) <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2021-10-14 14:25:03 +03:00
Paweł Redzyński a1b146669c plots: load templates from package (#6550)
* plots: vega: make interactive linear default plot

* plots: stop dumping default templates

* plots: move templates to package initial

* plots: update default plots schema

* plots: write defaults on init

* templates: migrate to importlib.resources

* fixup

* fixup
2021-10-14 08:40:31 +02:00
Peter Rowlands (변기호) b8478a8b04 machine: re-add instance dir management from TPI (#6748)
* machine: re-add instance dir management from TPI

* update default constructor

* machine: lazy load Repo.machine
2021-10-14 12:40:30 +09:00
Michael Lingelbach 0ae9130052 dvc: add 'repos' list to pre-commit config if it does not exist (#6795) 2021-10-13 22:50:25 +03:00
Batuhan Taskaya af34f5a0f1 webdav: limit httpx to fix the CI (#6800) 2021-10-13 16:49:54 +03:00
Ruslan Kuprieiev e9a7e0657d dvc: drop 3.6 support (#6792)
* dvc: drop 3.6 support

Considering that 3.6 has ~2 months until EOL, and our deps are
dropping support for it already (e.g. pygit2), we are droppipping
it as well. This will reduce the load on our test CI as well, since
we've started supporting 3.10 recently.

* setup.cfg: adjust requirements

* daemon: remove 3.6 logic

* tests: utils: remove pre 3.7 logic
2021-10-12 18:53:28 +03:00
Saugat PachhaiandRuslan Kuprieiev 8abda93e3e use setuptools_scm and build_requires (#6775)
* use setuptools_scm and build_requires

* remove kwargs from setup.py

* fix tests; fetch in depth in github actions

* use build to build sdist/wheel

* try updating pip and setuptools

* update pip

* remove or unpin wheel

* bring back the version in pyinstaller

* Update scripts/build.py

Co-authored-by: Ruslan Kuprieiev <[email protected]>
2021-10-12 17:58:14 +03:00
Ruslan Kuprieiev 53255c22b3 gha: tests: temporarily disable some remote tests (#6789)
* gha: tests: temporarily disable some remote tests

See #6788

* machine: upgrade tpi version

```
dvc/machine/backend/terraform.py:28:19: E1101: Instance of 'TerraformBackend' has no '_default_resource' member; maybe 'default_resource'? (no-member)
```

* tests: remotes: require --s3 for s3 tests
2021-10-12 17:26:47 +03:00
Paweł RedzyńskiandJorge Orpinel 6da15629c4 plots: modify: add note on modyfying image type plots (#6742)
* plots: modify: add note on modyfying image type plots

* Update dvc/command/plots.py

Co-authored-by: Jorge Orpinel <[email protected]>

Co-authored-by: Jorge Orpinel <[email protected]>
2021-10-12 16:08:49 +03:00
Ruslan Kuprieiev 5cfe61928e tree: use oids instead of objects (#6785) 2021-10-12 05:39:10 +03:00
Ruslan Kuprieiev 32467ada32 objects: extract size/nfiles to Meta (#6784)
* stage: capture metadata

* tree: capture metadata

* objects: extract size/nfiles from HashInfo/HashFile/Tree to Meta
2021-10-12 03:06:18 +03:00
pre-commit-ci[bot] 5cbaa8d98e [pre-commit.ci] pre-commit autoupdate (#6782)
updates:
- [github.com/pre-commit/mirrors-mypy: v0.910 → v0.910-1](https://github.com/pre-commit/mirrors-mypy/compare/v0.910...v0.910-1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2021-10-12 02:27:56 +03:00
Ruslan Kuprieiev 4762c38ff7 dvc: bump to 2.8.1 2021-10-11 16:09:51 +03:00
Ruslan Kuprieiev 66acbc892a setup.cfg: use text/x-rst instead of text/rst
Per https://packaging.python.org/specifications/core-metadata/#description-content-type
2021-10-11 16:09:29 +03:00
Ruslan Kuprieiev d2edbae7e3 dvc: bump to 2.8.0 2021-10-11 15:47:58 +03:00
Batuhan Taskaya be0c827b95 fs: add basic type annotations to FsspecWrapper (#6779) 2021-10-11 15:15:56 +03:00
Paweł Redzyński d6ecef7169 plots: fix wrong link (#6693) 2021-10-11 12:22:20 +02:00
eggqq007andZhenyu Gao e8b649a75f Fix exp show --sort-by issue #6675 (#6764)
Co-authored-by: Zhenyu Gao <[email protected]>
2021-10-11 16:46:38 +09:00
Batuhan Taskaya 8767f42f39 gs: use 3-char prefixes (#6759)
Resolves #6691. Normally it should have inherited this from the `ObjectFSWrapper`, but this seems like a leftover.
2021-10-11 16:16:17 +09:00
Paweł Redzyński 8580e66d60 gha: move cancelaltion into other workflows (#6771) 2021-10-08 18:03:07 +03:00
Saugat Pachhai c24ae2b0c3 exp init: show defaults in help message (#6763) 2021-10-08 20:14:12 +05:45
Saugat Pachhai 4e17279440 exp init: add config support (#6761) 2021-10-08 20:14:01 +05:45
Batuhan Taskaya 50cdf4f213 fsspec: normalize checksum() to return string (#6767) 2021-10-08 16:57:37 +03:00
Saugat Pachhai f628f670c9 advertise Python 3.10 support (#6772)
* advertise Python 3.10 support

Pygit2 does not have windows wheels yet and no pyarrow
for 3.10. But these platform-dependent things and
remote-dependent things should not stop us from
advertising support as we are pure-python based
package and we cannot claim support for all of our
dependencies.

* set pygit2 for Python 3.6 to <1.7
2021-10-08 15:50:05 +03:00
Batuhan Taskaya d7489bb65f hdfs: enable mocked hdfs tests by default (#6766) 2021-10-08 12:42:41 +02:00
dependabot[bot]andPaweł Redzyński 5a4f4d3a8e build(deps): Bump filelock from 3.2.0 to 3.3.0 (#6737)
* build(deps): Bump filelock from 3.2.0 to 3.3.0

Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.2.0...3.3.0)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

* tests: remotes: ignore abstract class instantiation for filelock

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Paweł Redzyński <[email protected]>
2021-10-08 15:36:33 +05:45
dependabot[bot] 2c24b24562 build(deps-dev): Bump types-requests from 2.25.6 to 2.25.9 (#6770)
Bumps [types-requests](https://github.com/python/typeshed) from 2.25.6 to 2.25.9.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-08 14:59:09 +05:45
dependabot[bot] df9f76c2c0 build(deps-dev): Bump types-paramiko from 0.1.9 to 2.7.1 (#6769)
Bumps [types-paramiko](https://github.com/python/typeshed) from 0.1.9 to 2.7.1.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-paramiko
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-08 14:58:19 +05:45
dependabot[bot] 0db3ece060 build(deps-dev): Bump types-toml from 0.1.5 to 0.10.0 (#6768)
Bumps [types-toml](https://github.com/python/typeshed) from 0.1.5 to 0.10.0.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-toml
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-08 14:58:08 +05:45
Saugat Pachhai 7c437cc349 make dependabot updates work (#6760)
Dependabot was throwing Illformed requirements `"< '3.10'"` for the
hdfs. But I noticed that it did accept other requirements where we
do similar. So it seems the indentation fixes it.

Even then, dependabot did not update any dependencies. I removed
setup.py file, after which immediately it started creating PRs.
So, I recknoed that it must have been due to some mechanism
in place from dependabot to disallow execution, so I tried to
remove few blocks of code from setup.py. And, I was eventually
able to figure out that it was from the block where we import version.

So, I added a try-except block and it magically again worked. :)

Let's see how it goes.
2021-10-08 14:46:47 +05:45
Saugat Pachhai 3ed5efceb7 move defaults to the class level (#6762) 2021-10-08 12:18:28 +05:45
dependabot[bot]andPaweł Redzyński d33df60e41 build(deps): Bump dvclive from 0.3.0 to 0.4.0 (#6753)
* build(deps): Bump dvclive from 0.3.0 to 0.4.0

Bumps [dvclive](https://github.com/iterative/dvclive) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/iterative/dvclive/releases)
- [Commits](https://github.com/iterative/dvclive/compare/0.3.0...0.4.0)

---
updated-dependencies:
- dependency-name: dvclive
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

* live: adjust tests to dvclive==0.4.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Paweł Redzyński <[email protected]>
2021-10-08 07:02:54 +05:45
Saugat Pachhai c5415342d0 move requirements to setup.cfg (#6758)
* move requirements to setup.cfg

* hash based on setup.cfg

* do not break webhdfs installation
2021-10-08 07:00:14 +05:45
Paweł Redzyński dd86076ba6 gha: cancel previous workflows (#6756) 2021-10-07 17:59:44 +03:00
Ruslan Kuprieiev 7dc3859400 pyinstaller: hooks: remove webhdfs 2021-10-07 17:30:22 +03:00
Batuhan Taskaya 4f3b28e268 webhdfs: migrate to fsspec (#6662)
* webhdfs: migrate to fsspec

* skip the tests that can't use hdfs
2021-10-07 17:02:31 +03:00
Ruslan Kuprieiev 32f435c47f gha: benchmarks: fix pytest flags (#6749)
Kudos @skshetry for catching.
2021-10-06 17:07:41 +03:00
Batuhan Taskaya 054feae778 hdfs: migrate to fsspec (#6604)
* hdfs: migrate to fsspec

* normalize windows paths when delegating back to fsspec
2021-10-06 15:44:27 +03:00
Saugat Pachhai 2abe515391 Pin dvclive to 0.3.0 2021-10-06 14:28:36 +05:45
eggqq007andZhenyu Gao 1eb89a2dc0 command: Should not consume 'print' to output default remote info (#6650)
1. should not consume 'print' to output default remote info,
   otherwise the --quiet would not work.

Co-authored-by: Zhenyu Gao <[email protected]>
2021-10-06 09:27:47 +05:45
eggqq007andZhenyu Gao 9c300a0a1c command: output prettify json in tty (#6743)
1. update diff.py,experiments.py,metrics.py,params.py,status.py to
print prettify json
2. rewrite write_json in __init__.py to accept default function
3. add new unit test for write_json in tests/unit/ui/test_console.py
4. Fix #6712

Co-authored-by: Zhenyu Gao <[email protected]>
2021-10-05 20:19:56 +05:45
Ruslan Kuprieiev 7b68a6f0ca gha: run benchmarks on PRs (#6733) 2021-10-05 14:18:43 +03:00
Saugat Pachhai ee823a59b9 Run on Python3.10 (#6745) 2021-10-05 16:46:53 +05:45
David de la Iglesia CastroandSaugat Pachhai 16f404b16a Fix dvclive running subdir (#6740)
* Fix dvclive running subdir

* Apply suggestions from code review

Co-authored-by: Saugat Pachhai <[email protected]>

* Applied suggestions

* Update tests/func/test_live.py

Co-authored-by: Saugat Pachhai <[email protected]>
2021-10-05 13:16:33 +05:45
Saugat Pachhai 0eda4bd91c exp init: only ask for that are not provided in an interactive mode (#6739) 2021-10-04 14:33:08 +05:45
Saugat Pachhai a098a50c8d move exp init logic to dvc.repo.experiments.init (#6738) 2021-10-04 12:26:34 +05:45
dependabot[bot] f57a378c0c build(deps): Bump jaraco-windows from 5.6.0 to 5.7.0 (#6735)
Bumps [jaraco-windows](https://github.com/jaraco/jaraco.windows) from 5.6.0 to 5.7.0.
- [Release notes](https://github.com/jaraco/jaraco.windows/releases)
- [Changelog](https://github.com/jaraco/jaraco.windows/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/jaraco.windows/compare/v5.6.0...v5.7.0)

---
updated-dependencies:
- dependency-name: jaraco-windows
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-04 10:12:47 +05:45
dependabot[bot] 3e0a50e52e build(deps): Bump pytest-cov from 2.12.1 to 3.0.0 (#6736)
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.12.1 to 3.0.0.
- [Release notes](https://github.com/pytest-dev/pytest-cov/releases)
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.12.1...v3.0.0)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-04 09:31:27 +05:45
Guillaume COTER ec72866eb9 Bump typing_extensions to >=3.7.4 (#6731) (#6732) 2021-10-03 20:10:28 +05:45
Batuhan Taskaya e53d390ff9 http: explicitly ignore all http-related errors (from server side to client connection drops) (#6728) 2021-10-02 20:42:23 +03:00
Batuhan Taskaya dd330ac592 setup: bump fsspec/s3fs/gcsfs (#6727) 2021-10-02 18:54:09 +02:00
pwoolvett 4ccb712b6d build(setup): avoid leaking tests package into site-packages (#6585)
Signed-off-by: Pablo Woolvett <[email protected]>
2021-10-02 16:46:45 +03:00
Saugat Pachhai ecee2d3522 Move static metadata to setup.cfg (#6581) 2021-10-02 16:08:30 +03:00
20bb6ddf3c doctor: fixes unknown cache directory error on some windows mapped network shares (#6364)
* doctor: fixes unknown cache director error on some windows mapped network shares

On Windows, resolve calls os.path._getfinalpathname, which calls GetFinalPathNameByHandle.
This starts from the final resolved path of the file in the NT namespace (e.g. \Device\Mup\server\share...)
and works backward as required to get the VOLUME_NAME_DOS. If the final path is a UNC share (i.e. \Device\Mup),
We fix this by calling resolve() also on the partition mountpoints
We also take the opportunity to make checks in case a PermissionError is returned to us

Fixes github.com/iterative/dvc/issues/6305

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* doctor: fixes unknown cache director error on some windows mapped network shares

On Windows, resolve calls os.path._getfinalpathname, which calls GetFinalPathNameByHandle.
This starts from the final resolved path of the file in the NT namespace (e.g. \Device\Mup\server\share...)
and works backward as required to get the VOLUME_NAME_DOS. If the final path is a UNC share (i.e. \Device\Mup),
We fix this by calling resolve() also on the partition mountpoints
We also take the opportunity to make checks in case a PermissionError is returned to us

Fixes #6305

Co-authored-by: CHETOUAN <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: CHETOUAN <[email protected]>
2021-10-02 16:06:42 +03:00
Saugat Pachhai e58fcd864b [WIP] exp init: introduce --with={default,live} flag (#6716)
* exp init: introduce `--with={default,live}` flag

This flag will select the different mode of outputs of stage.
With `--with=live`, it'll enable `live` setting in the stage.
On interactive mode, it'll always ask user if they want to
use live (unless `--with=live` is specified), and then it'll
skip metrics/plots question and moves on to ask for path to
dvclive path.

Also, if live setting is enabled in the stage, it'll set the
`models` to be a checkpoint output.

* s/with/type and don't ask for live interactively
2021-10-02 16:12:44 +05:45
Saugat Pachhai 0514db3bfa deps: loose requirements for dataclasses and aiohttp-retry (#6724) 2021-10-01 17:00:03 +05:45
Saugat Pachhai 1e0b2d7af0 benedict: monkeypatch, performance improvements, bugfixes (#6706)
Fixes #6476, fixes #6423 and closes #6484.
2021-10-01 18:16:47 +09:00
Casper da Costa-Luis bfcc8f877c add licence year + company (#6717) 2021-10-01 09:54:55 +02:00
Saugat Pachhai 46ad8745d3 Use latest pygit2 from master for 3.10 (#6723)
* Use latest pygit2 from master for 3.10

* ci: update brew
2021-10-01 11:48:11 +05:45
dependabot[bot] eb13d3ca83 build(deps): Bump google-cloud-storage from 1.42.2 to 1.42.3 (#6722)
Bumps [google-cloud-storage](https://github.com/googleapis/python-storage) from 1.42.2 to 1.42.3.
- [Release notes](https://github.com/googleapis/python-storage/releases)
- [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-storage/compare/v1.42.2...v1.42.3)

---
updated-dependencies:
- dependency-name: google-cloud-storage
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-01 08:27:40 +05:45
Ruslan Kuprieiev f46beb4958 checkout: move to objects (#6721)
Checkout operation is pretty much the opposite of staging, so it too belongs in objects.
2021-09-30 23:06:03 +03:00
Saugat Pachhai 9a1d74048a cli: add short alias for --show-{csv,json,md} (#6711)
As `--csv`/`--json`/`--md`
2021-09-30 20:38:32 +05:45
dependabot[bot] cce6aed378 build(deps): Bump filelock from 3.1.0 to 3.2.0 (#6710)
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.1.0...3.2.0)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-30 08:49:39 +05:45
Saugat Pachhai 612696cf4a deps: add contextvars as a dependency for Python3.6 (#6705) 2021-09-29 10:39:14 +05:45
Saugat Pachhai (सौगात) 3f5f983f5f ui: import sys on write_json 2021-09-29 09:59:30 +05:45
eggqq007andZhenyu Gao fed9f088ca command: Show dvc list output with pretty JSON format if output to tty (#6647)
1. update ls __init__.py to output pretty JSON format
2. introduce write_json function into ui __init__.py

Co-authored-by: Zhenyu Gao <[email protected]>
2021-09-29 08:44:33 +05:45
Saugat Pachhai 05eaeef012 utils.http: remove open url (#6700)
This was used before on opening http urls as a stream,
but we no longer use those (as we use fsspec's open method).

One thing to note however is that, compared to previous
implementation, fsspec's http filesystem's open is not very
robust to failures.
2021-09-28 19:19:52 +05:45
Saugat Pachhai 48c0527755 exp init: show heading based on the presence of dvc.yaml file (#6701) 2021-09-28 15:11:58 +05:45
Saugat Pachhai 4668a35f64 exp init: support interactive and explicit options (#6681) 2021-09-28 14:36:21 +05:45
dependabot[bot] 8107ec849b build(deps): Bump filelock from 3.0.12 to 3.1.0 (#6698)
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.0.12 to 3.1.0.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Commits](https://github.com/tox-dev/py-filelock/compare/v3.0.12...3.1.0)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-28 14:18:17 +05:45
dependabot[bot]andSaugat Pachhai 19284b3b4c build(deps): Bump urllib3 from 1.26.6 to 1.26.7 (#6697)
* build(deps): Bump urllib3 from 1.26.6 to 1.26.7

Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.6 to 1.26.7.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.6...1.26.7)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

* Get rid of urllib3 as a test dependency

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Saugat Pachhai <[email protected]>
2021-09-28 13:52:02 +05:45
dependabot[bot] 315d1cde99 build(deps): Bump pytest-xdist from 2.3.0 to 2.4.0 (#6696)
Bumps [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/pytest-dev/pytest-xdist/releases)
- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: pytest-xdist
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-28 07:50:59 +00:00
dependabot[bot] 1d415efd0b build(deps): Bump google-cloud-storage from 1.42.1 to 1.42.2 (#6635)
Bumps [google-cloud-storage](https://github.com/googleapis/python-storage) from 1.42.1 to 1.42.2.
- [Release notes](https://github.com/googleapis/python-storage/releases)
- [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-storage/compare/v1.42.1...v1.42.2)

---
updated-dependencies:
- dependency-name: google-cloud-storage
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-28 13:12:18 +05:45
Saugat Pachhai e7c20aa1d8 better exception messages for schema errors (#6679)
* Generate prettier schema error messages

* add test for #6285
2021-09-28 11:12:16 +05:45
Saugat Pachhai 058377f33e ui: support pager universally (#6692) 2021-09-27 18:11:20 +05:45
Saugat Pachhai c7b1b14905 ui: use ui.rich_text instead of rich.text.Text (#6690) 2021-09-27 12:04:19 +05:45
Amey Kulkarni fbd8f5c185 Issue 6651: Added alias ls for dvc list (#6683) 2021-09-25 21:14:11 +03:00
whysage 7fe370b729 remote: add support for Amazon S3 timeout configuration (#6634)
Fixes #6241
2021-09-25 14:06:43 +03:00
Batuhan Taskaya f9df6d2a16 http: allow reading proxies from the current environment; (#6688) 2021-09-25 11:32:44 +02:00
Ruslan Kuprieiev 2e4fb376da system: get rid of speedcopy (#6686)
speedcopy only patches shutil.copyfile, which we don't even use
anywhere because it lacks callbacks/progress and we instead use
our own copyfile implementation, so this is totally useless for
us and can be removed.
2021-09-25 04:42:45 +03:00
Ruslan Kuprieiev 6d6a3f17b1 system: remove legacy code (#6685) 2021-09-25 03:04:54 +03:00
Ruslan Kuprieiev bdaf4aced6 state: cleanup redundant or localfs-specific logic (#6684)
Pre-requisite for making state work with other filesystems.
2021-09-25 02:11:35 +03:00
Batuhan Taskaya 0bf1802d4b ssh: fix path normalization on Windows (#6517) 2021-09-23 18:23:40 +03:00
Batuhan Taskaya 6d3045320b ssh: use a docker-based testing environment (#6677) 2021-09-23 16:00:48 +03:00
Batuhan Taskaya 3469dda8b8 ssh: bundle bcrypt by default (#6678) 2021-09-23 11:00:13 +02:00
Saugat Pachhai eefede92b2 merge yaml parsing and schema validation into one, aka strictyaml.load (#6661) 2021-09-23 00:03:25 +03:00
Ruslan Kuprieiev e295434ec6 checkout: start switching to obj-based workflow (#6648)
* webhdfs: info/checksum: properly raise FileNotFoundError

* checkout: start switching to obj-based workflow

Related #6594
2021-09-22 02:16:29 +03:00
Saugat Pachhai b1f1b01ffc debug: add yappi, callgrind output support (#6664)
* debug: add yappi, callgrind output support

* support custom path and callables
2021-09-21 20:33:08 +03:00
Casper da Costa-Luis f41d7effdb fix dvc[all] (#6657)
* fix dvc[all]

- related https://github.com/iterative/dvc-snap/issues/24

* fix pylint
2021-09-21 18:54:55 +03:00
Saugat Pachhai 6a417885b8 exceptional exceptions (#6654)
* exceptional exceptions

* backward compatibility with previous exceptions

* make linters happy :)
2021-09-21 11:27:57 +05:45
pre-commit-ci[bot] 4221d9f70b [pre-commit.ci] pre-commit autoupdate (#6659)
updates:
- [github.com/ambv/black: 21.8b0 → 21.9b0](https://github.com/ambv/black/compare/21.8b0...21.9b0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2021-09-21 09:28:51 +05:45
Ruslan Kuprieiev 5822d53687 tests: upgrade azurite
```
2021-09-21T00:15:31.6789059Z ____________________ ERROR at setup of test_get_url[azure] _____________________
2021-09-21T00:15:31.6790443Z [gw0] linux -- Python 3.7.12 /opt/hostedtoolcache/Python/3.7.12/x64/bin/python
2021-09-21T00:15:31.6791202Z 
2021-09-21T00:15:31.6792291Z request = <FixtureRequest for <Function test_get_url[azure]>>
2021-09-21T00:15:31.6793561Z 
2021-09-21T00:15:31.6794120Z     def fill(request):
2021-09-21T00:15:31.6794792Z         item = request._pyfuncitem
2021-09-21T00:15:31.6795627Z         fixturenames = getattr(item, "fixturenames", None)
2021-09-21T00:15:31.6797090Z         if fixturenames is None:
2021-09-21T00:15:31.6797922Z             fixturenames = request.fixturenames
2021-09-21T00:15:31.6798497Z     
2021-09-21T00:15:31.6799393Z         if hasattr(item, 'callspec'):
2021-09-21T00:15:31.6800256Z             for param, val in sorted_by_dependency(item.callspec.params, fixturenames):
2021-09-21T00:15:31.6801309Z                 if val is not None and is_lazy_fixture(val):
2021-09-21T00:15:31.6802233Z >                   item.callspec.params[param] = request.getfixturevalue(val.name)
2021-09-21T00:15:31.6802912Z 
2021-09-21T00:15:31.6803937Z /opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/site-packages/pytest_lazyfixture.py:35: 
2021-09-21T00:15:31.6804952Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2021-09-21T00:15:31.6806085Z /home/runner/work/dvc/dvc/tests/remotes/azure.py:102: in azure
2021-09-21T00:15:31.6807090Z     container.get_container_properties()
2021-09-21T00:15:31.6808748Z /opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/site-packages/azure/core/tracing/decorator.py:83: in wrapper_use_tracer
2021-09-21T00:15:31.6822551Z     return func(*args, **kwargs)
2021-09-21T00:15:31.6824143Z /opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/site-packages/azure/storage/blob/_container_client.py:497: in get_container_properties
2021-09-21T00:15:31.6825038Z     process_storage_error(error)
2021-09-21T00:15:31.6825652Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2021-09-21T00:15:31.6825908Z 
2021-09-21T00:15:31.6826916Z storage_error = HttpResponseError("Operation returned an invalid status 'The value for one of the HTTP headers is not in the correct format.'")
2021-09-21T00:15:31.6827586Z 
2021-09-21T00:15:31.6828351Z     def process_storage_error(storage_error):   # pylint:disable=too-many-statements
2021-09-21T00:15:31.6829086Z         raise_error = HttpResponseError
2021-09-21T00:15:31.6829597Z         serialized = False
2021-09-21T00:15:31.6830066Z         if not storage_error.response:
2021-09-21T00:15:31.6830554Z             raise storage_error
2021-09-21T00:15:31.6831558Z         # If it is one of those three then it has been serialized prior by the generated layer.
2021-09-21T00:15:31.6832480Z         if isinstance(storage_error, (PartialBatchErrorException,
2021-09-21T00:15:31.6833636Z                                       ClientAuthenticationError, ResourceNotFoundError, ResourceExistsError)):
2021-09-21T00:15:31.6834687Z             serialized = True
2021-09-21T00:15:31.6835753Z         error_code = storage_error.response.headers.get('x-ms-error-code')
2021-09-21T00:15:31.6836524Z         error_message = storage_error.message
2021-09-21T00:15:31.6837001Z         additional_data = {}
2021-09-21T00:15:31.6837398Z         error_dict = {}
2021-09-21T00:15:31.6837739Z         try:
2021-09-21T00:15:31.6838506Z             error_body = ContentDecodePolicy.deserialize_from_http_generics(storage_error.response)
2021-09-21T00:15:31.6839345Z             # If it is an XML response
2021-09-21T00:15:31.6839837Z             if isinstance(error_body, Element):
2021-09-21T00:15:31.6840285Z                 error_dict = {
2021-09-21T00:15:31.6840750Z                     child.tag.lower(): child.text
2021-09-21T00:15:31.6841435Z                     for child in error_body
2021-09-21T00:15:31.6841822Z                 }
2021-09-21T00:15:31.6842195Z             # If it is a JSON response
2021-09-21T00:15:31.6842692Z             elif isinstance(error_body, dict):
2021-09-21T00:15:31.6843413Z                 error_dict = error_body.get('error', {})
2021-09-21T00:15:31.6843893Z             elif not error_code:
2021-09-21T00:15:31.6844347Z                 _LOGGER.warning(
2021-09-21T00:15:31.6845611Z                     'Unexpected return type % from ContentDecodePolicy.deserialize_from_http_generics.', type(error_body))
2021-09-21T00:15:31.6846887Z                 error_dict = {'message': str(error_body)}
2021-09-21T00:15:31.6847294Z     
2021-09-21T00:15:31.6847730Z             # If we extracted from a Json or XML response
2021-09-21T00:15:31.6848205Z             if error_dict:
2021-09-21T00:15:31.6848792Z                 error_code = error_dict.get('code')
2021-09-21T00:15:31.6849704Z                 error_message = error_dict.get('message')
2021-09-21T00:15:31.6850578Z                 additional_data = {k: v for k, v in error_dict.items() if k not in {'code', 'message'}}
2021-09-21T00:15:31.6851392Z         except DecodeError:
2021-09-21T00:15:31.6851801Z             pass
2021-09-21T00:15:31.6852290Z     
2021-09-21T00:15:31.6852601Z         try:
2021-09-21T00:15:31.6853156Z             # This check would be unnecessary if we have already serialized the error
2021-09-21T00:15:31.6853821Z             if error_code and not serialized:
2021-09-21T00:15:31.6854423Z                 error_code = StorageErrorCode(error_code)
2021-09-21T00:15:31.6855164Z                 if error_code in [StorageErrorCode.condition_not_met,
2021-09-21T00:15:31.6856229Z                                   StorageErrorCode.blob_overwritten]:
2021-09-21T00:15:31.6856938Z                     raise_error = ResourceModifiedError
2021-09-21T00:15:31.6857739Z                 if error_code in [StorageErrorCode.invalid_authentication_info,
2021-09-21T00:15:31.6858641Z                                   StorageErrorCode.authentication_failed]:
2021-09-21T00:15:31.6859468Z                     raise_error = ClientAuthenticationError
2021-09-21T00:15:31.6860256Z                 if error_code in [StorageErrorCode.resource_not_found,
2021-09-21T00:15:31.6861256Z                                   StorageErrorCode.cannot_verify_copy_source,
2021-09-21T00:15:31.6861998Z                                   StorageErrorCode.blob_not_found,
2021-09-21T00:15:31.6862716Z                                   StorageErrorCode.queue_not_found,
2021-09-21T00:15:31.6863718Z                                   StorageErrorCode.container_not_found,
2021-09-21T00:15:31.6864526Z                                   StorageErrorCode.parent_not_found,
2021-09-21T00:15:31.6865432Z                                   StorageErrorCode.share_not_found]:
2021-09-21T00:15:31.6866549Z                     raise_error = ResourceNotFoundError
2021-09-21T00:15:31.6867609Z                 if error_code in [StorageErrorCode.account_already_exists,
2021-09-21T00:15:31.6868499Z                                   StorageErrorCode.account_being_created,
2021-09-21T00:15:31.6869383Z                                   StorageErrorCode.resource_already_exists,
2021-09-21T00:15:31.6870728Z                                   StorageErrorCode.resource_type_mismatch,
2021-09-21T00:15:31.6871688Z                                   StorageErrorCode.blob_already_exists,
2021-09-21T00:15:31.6872429Z                                   StorageErrorCode.queue_already_exists,
2021-09-21T00:15:31.6873252Z                                   StorageErrorCode.container_already_exists,
2021-09-21T00:15:31.6874124Z                                   StorageErrorCode.container_being_deleted,
2021-09-21T00:15:31.6874922Z                                   StorageErrorCode.queue_being_deleted,
2021-09-21T00:15:31.6875857Z                                   StorageErrorCode.share_already_exists,
2021-09-21T00:15:31.6876608Z                                   StorageErrorCode.share_being_deleted]:
2021-09-21T00:15:31.6877289Z                     raise_error = ResourceExistsError
2021-09-21T00:15:31.6877807Z         except ValueError:
2021-09-21T00:15:31.6878259Z             # Got an unknown error code
2021-09-21T00:15:31.6878648Z             pass
2021-09-21T00:15:31.6878942Z     
2021-09-21T00:15:31.6879424Z         # Error message should include all the error properties
2021-09-21T00:15:31.6879908Z         try:
2021-09-21T00:15:31.6880416Z             error_message += "\nErrorCode:{}".format(error_code.value)
2021-09-21T00:15:31.6881289Z         except AttributeError:
2021-09-21T00:15:31.6883082Z             error_message += "\nErrorCode:{}".format(error_code)
2021-09-21T00:15:31.6883807Z         for name, info in additional_data.items():
2021-09-21T00:15:31.6884446Z             error_message += "\n{}:{}".format(name, info)
2021-09-21T00:15:31.6884932Z     
2021-09-21T00:15:31.6885741Z         # No need to create an instance if it has already been serialized by the generated layer
2021-09-21T00:15:31.6886538Z         if serialized:
2021-09-21T00:15:31.6887019Z             storage_error.message = error_message
2021-09-21T00:15:31.6887526Z             error = storage_error
2021-09-21T00:15:31.6887908Z         else:
2021-09-21T00:15:31.6888499Z             error = raise_error(message=error_message, response=storage_error.response)
2021-09-21T00:15:31.6889380Z         # Ensure these properties are stored in the error instance as well (not just the error message)
2021-09-21T00:15:31.6890078Z         error.error_code = error_code
2021-09-21T00:15:31.6890630Z         error.additional_info = additional_data
2021-09-21T00:15:31.6891956Z         # error.args is what's surfaced on the traceback - show error message in all cases
2021-09-21T00:15:31.6892672Z         error.args = (error.message,)
2021-09-21T00:15:31.6893112Z         try:
2021-09-21T00:15:31.6893784Z             # `from None` prevents us from double printing the exception (suppresses generated layer error context)
2021-09-21T00:15:31.6894873Z >           exec("raise error from None")   # pylint: disable=exec-used # nosec
2021-09-21T00:15:31.6896126Z E           azure.core.exceptions.HttpResponseError: The value for one of the HTTP headers is not in the correct format.
2021-09-21T00:15:31.6897385Z E           RequestId:d83b8102-bde1-4a77-aa7a-b67bbc2c4a80
2021-09-21T00:15:31.6898080Z E           Time:2021-09-21T00:09:53.815Z
2021-09-21T00:15:31.6898578Z E           ErrorCode:InvalidHeaderValue
2021-09-21T00:15:31.6899259Z E           headername:x-ms-version
2021-09-21T00:15:31.6900066Z E           headervalue:2020-10-02
2021-09-21T00:15:31.6900355Z 
2021-09-21T00:15:31.6901781Z /opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/site-packages/azure/storage/blob/_shared/response_handlers.py:177: HttpResponseError
```
2021-09-21 03:53:06 +03:00
Batuhan Taskaya a478a0ea98 progress: ignore when set_size callback is called with None (#6612) 2021-09-21 03:34:17 +03:00
Ruslan Kuprieiev e81c88e501 gha: increase timeouts (#6645) 2021-09-19 23:47:15 +03:00
b286c311f4 reincorporate PR 6111 into current master, re: #4420 (#6419)
* rebased PR 6111 onto master

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* use make_tmp_dir fixture in tests

Co-authored-by: U119511 <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alexander Meier <[email protected]>
Co-authored-by: meierale <[email protected]>
2021-09-18 22:36:50 +03:00
507 changed files with 20471 additions and 26012 deletions
+4
View File
@@ -0,0 +1,4 @@
/tests
/dvc
/.github
/bin
+1
View File
@@ -0,0 +1 @@
ref-names: $Format:%D$
+1
View File
@@ -0,0 +1 @@
.git_archival.txt export-subst
-3
View File
@@ -1,3 +0,0 @@
# These are supported funding model platforms
patreon: DVCorg
+1 -1
View File
@@ -1,7 +1,7 @@
pull_request_rules:
- name: backport patches to 1.11
conditions:
- base=master
- base=main
- label=backport-1.11
actions:
backport:
+23
View File
@@ -0,0 +1,23 @@
name: benchmarks
on: [pull_request, workflow_dispatch]
jobs:
bench:
name: run benchmarks
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-18.04]
steps:
- name: Cancel Previous Runs
uses: styfle/[email protected]
with:
access_token: ${{ github.token }}
- uses: actions/setup-python@v4
with:
python-version: 3.8
- uses: iterative/dvc-bench@main
with:
pytest_options: "-k 'test_init or test_help'"
+34 -22
View File
@@ -2,7 +2,7 @@ name: Build packages
on:
push:
branches:
- master
- main
release:
types: [released, prereleased]
workflow_dispatch:
@@ -10,11 +10,13 @@ jobs:
deb:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2.3.4
- name: Set up Python 3.8
uses: actions/[email protected]
- uses: actions/checkout@v3
with:
python-version: 3.8
fetch-depth: 0
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Set up Ruby 2.4
uses: actions/[email protected]
with:
@@ -39,11 +41,13 @@ jobs:
rpm:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2.3.4
- name: Set up Python 3.8
uses: actions/[email protected]
- uses: actions/checkout@v3
with:
python-version: 3.8
fetch-depth: 0
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Set up Ruby 2.4
uses: actions/[email protected]
with:
@@ -68,11 +72,13 @@ jobs:
osxpkg:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2.3.4
- name: Set up Python 3.8
uses: actions/[email protected]
- uses: actions/checkout@v3
with:
python-version: 3.8
fetch-depth: 0
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Set up Ruby 2.4
uses: actions/[email protected]
with:
@@ -100,11 +106,13 @@ jobs:
exe:
runs-on: windows-2019
steps:
- uses: actions/checkout@v2.3.4
- name: Set up Python 3.8
uses: actions/[email protected]
- uses: actions/checkout@v3
with:
python-version: 3.8
fetch-depth: 0
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Install deps
run: |
pip install .[all]
@@ -128,15 +136,19 @@ jobs:
pip:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2.3.4
- name: Set up Python 3.7
uses: actions/[email protected]
- uses: actions/checkout@v3
with:
python-version: 3.7
fetch-depth: 0
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Install
run: pip install ".[all,tests]"
- name: Build packages
run: ./scripts/build_package.sh
run: |
pip install -U pip
./scripts/build_package.sh
- name: Publish packages to PyPI
if: github.event_name == 'release'
uses: pypa/gh-action-pypi-publish@master
+27 -64
View File
@@ -16,43 +16,39 @@ jobs:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- name: Cancel Previous Runs
uses: styfle/[email protected]
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python 3.8
uses: actions/setup-python@v2.2.2
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Install requirements
run: |
pip install wheel
pip install ".[all,tests,terraform]" Pygments collective.checkdocs pre-commit
- name: install temporary dependencies
run: |
pip install git+https://github.com/isidentical/gdrivefs@service-account
pip install git+https://github.com/isidentical/pydata-google-auth@service-account
pip install --upgrade pip setuptools wheel
pip install collective.checkdocs==0.2
pip install ".[dev]" pre-commit
- name: Check README
run: python setup.py checkdocs
- uses: pre-commit/action@v2.0.3
- uses: pre-commit/action@v3.0.0
tests:
timeout-minutes: 45
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-18.04, windows-2019, macos-10.15]
pyv: ["3.6", "3.7", "3.8", "3.9"]
include:
- os: "macos-10.15"
pyv: "3.10.0-rc.1"
exclude:
- os: macos-10.15
pyv: "3.9"
- os: macos-10.15
pyv: "3.7"
os: [ubuntu-20.04, windows-latest, macos-latest]
pyv: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2.2.2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.pyv }}
- name: get pip cache dir
@@ -61,63 +57,30 @@ jobs:
echo "::set-output name=dir::$(pip cache dir)"
- name: set pip cache
id: pip-cache
uses: actions/cache@v2.1.6
uses: actions/cache@v3
with:
path: ${{ steps.pip-cache-dir.outputs.dir }}
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
key: ${{ runner.os }}-pip-${{ hashFiles('setup.cfg') }}
restore-keys: |
${{ runner.os }}-pip-
- name: install libgit2 on Python3.10 in macOS
if: matrix.pyv == '3.10.0-rc.1' && matrix.os == 'macos-10.15'
run: |
brew install libgit2
- name: install
if: steps.cache.pip-cache-dir.cache-hit != 'true'
run: |
pip install --upgrade pip setuptools wheel
pip install -e ".[all,tests,terraform]"
- name: install temporary dependencies
run: |
pip install git+https://github.com/isidentical/gdrivefs@service-account
pip install git+https://github.com/isidentical/pydata-google-auth@service-account
- name: setup git
run: |
git config --global user.email "[email protected]"
git config --global user.name "DVC Tester"
- name: setup gs creds
shell: bash
env:
GS_CREDS_JSON: ${{ secrets.GS_CREDS_JSON }}
if: env.GS_CREDS_JSON != ''
run: |
mkdir -p scripts/ci
echo "$GS_CREDS_JSON" > scripts/ci/gcp-creds.json
- name: disable gdrive tests
if: >-
github.event_name != 'schedule' &&
(matrix.pyv != '3.8' || matrix.os != 'ubuntu-18.04')
run: |
echo "extra_test_args=--disable-gdrive" >> $GITHUB_ENV
pip install -e ".[dev]"
- name: run tests
timeout-minutes: 30
env:
GDRIVE_CREDENTIALS_DATA: ${{ secrets.GDRIVE_CREDENTIALS_DATA }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
OSS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY_ID}}
OSS_ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET}}
OSS_ENDPOINT: ${{ secrets.OSS_ENDPOINT}}
timeout-minutes: 40
run: >-
python -m tests -n=4
python -m tests -n=auto
--cov-report=xml --cov-report=term
--all ${{ env.extra_test_args }}
${{ env.extra_test_args }}
- name: upload coverage report
uses: codecov/codecov-action@v2.1.0
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
fail_ci_if_error: false
notify:
if: github.ref == 'refs/heads/master' && failure()
if: github.ref == 'refs/heads/main' && failure()
needs:
- lint
- tests
@@ -128,6 +91,6 @@ jobs:
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: 'CI Failed on master :boom:'
SLACK_MESSAGE: 'CI Failed on main :boom:'
SLACK_TITLE: CI Status
SLACK_USERNAME: DVC-CI
+15 -3
View File
@@ -1,10 +1,13 @@
__pycache__/
neatlynx.conf
neatlynx/__pycache__
.idea
/cache
*.pyc
.env/
.env2.7/
.env*/
.venv
env/
venv/
.python-version
.dvc.conf.lock
@@ -13,6 +16,7 @@ build/
dist/
*.egg-info/
.eggs
hooks/*.pyc
@@ -25,6 +29,7 @@ innosetup/config.ini
.coverage
.coverage.*
coverage.xml
*.sw?
@@ -38,3 +43,10 @@ scripts/ci/gcp-creds.json
.gcp-creds.json
*~
/dvc/_dvc_version.py
.mypy_cache/
.pytest_cache/
.nox/
.tox/
htmlcov/
+16 -16
View File
@@ -1,24 +1,29 @@
ci:
skip: [pylint, dvc-pre-commit]
skip: [mypy, pylint, dvc-pre-commit]
repos:
- hooks:
- id: black
language_version: python3
repo: https://github.com/ambv/black
rev: 21.8b0
rev: 22.3.0
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-toml
- id: check-yaml
- hooks:
- id: codespell
args:
- --ignore-words-list
- ba,datas,fo,uptodate
- ba,datas,fo,uptodate,cachable
repo: https://github.com/codespell-project/codespell
rev: v2.1.0
- hooks:
- id: isort
language_version: python3
repo: https://github.com/timothycrosley/isort
rev: 5.9.3
rev: 5.10.1
- hooks:
- id: flake8
language_version: python3
@@ -29,19 +34,14 @@ repos:
- flake8-string-format
repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
- hooks:
- id: mypy
language_version: python3
files: ^dvc/
additional_dependencies:
- types-requests
- types-paramiko
- types-tabulate
- types-toml
repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v0.910'
- repo: local
hooks:
- id: mypy
name: mypy
entry: mypy
files: ^dvc/
language: system
types: [python]
- id: pylint
name: pylint
entry: pylint
@@ -54,7 +54,7 @@ repos:
id: beautysh
language_version: python3
repo: https://github.com/lovesegfault/beautysh
rev: v6.1.0
rev: v6.2.1
- hooks:
- args:
- git-hook
-615
View File
@@ -1,615 +0,0 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=pygit2
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
init-hook="import sys;
from pathlib import Path;
from pylint.config import find_pylintrc;
tests_folder = Path(find_pylintrc()).parent / 'tests'
sys.path.append(str(tests_folder));
"
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use.
jobs=1
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=pylint_pytest,pylint_plugin_disable
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=format,
# ^ most of them are covered by flake8,
# and are conventions rather than warnings or errors
refactoring, # will be noisy if it starts giving suggestions
spelling,
# design is too-few-* and too-many-* checks, too-much-* for us right now
design,
# following were generated by default
# most of them are redundant Py3 checks for Py2+3 codebase
print-statement,
parameter-unpacking,
unpacking-in-except,
old-raise-syntax,
backtick,
long-suffix,
old-ne-operator,
old-octal-literal,
import-star-module-level,
non-ascii-bytes-literal,
raw-checker-failed,
bad-inline-option,
locally-disabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
apply-builtin,
basestring-builtin,
buffer-builtin,
cmp-builtin,
coerce-builtin,
execfile-builtin,
file-builtin,
long-builtin,
raw_input-builtin,
reduce-builtin,
standarderror-builtin,
unicode-builtin,
xrange-builtin,
coerce-method,
delslice-method,
getslice-method,
setslice-method,
no-absolute-import,
old-division,
dict-iter-method,
dict-view-method,
next-method-called,
metaclass-assignment,
indexing-exception,
raising-string,
reload-builtin,
oct-method,
hex-method,
nonzero-method,
cmp-method,
input-builtin,
round-builtin,
intern-builtin,
unichr-builtin,
map-builtin-not-iterating,
zip-builtin-not-iterating,
range-builtin-not-iterating,
filter-builtin-not-iterating,
using-cmp-argument,
eq-without-hash,
div-method,
idiv-method,
rdiv-method,
exception-message-attribute,
invalid-str-codec,
sys-max-int,
bad-python3-import,
deprecated-string-function,
deprecated-str-translate-call,
deprecated-itertools-function,
deprecated-types-field,
next-method-defined,
dict-items-not-iterating,
dict-keys-not-iterating,
dict-values-not-iterating,
deprecated-operator-function,
deprecated-urllib-function,
xreadlines-attribute,
deprecated-sys-function,
exception-escape,
comprehension-escape,
# custom disables start here
no-self-use,
invalid-name,
# already a check enabled for `wildcard-import`
unused-wildcard-import,
misplaced-comparison-constant,
# probably soon: https://github.com/iterative/dvc/issues/1843
logging-format-interpolation,
logging-fstring-interpolation,
# we don't have proper docstrings yet
# even if we do, we'll be doing it gradually
missing-function-docstring,
missing-module-docstring,
missing-class-docstring,
duplicate-code,
# too noisy, and detects issues even when import is inside a function
# see: https://github.com/PyCQA/pylint/issues/850
cyclic-import,
# isort should cover these:
wrong-import-order,
wrong-import-position,
ungrouped-imports,
multiple-imports,
# we use it to speedup/optimize
import-outside-toplevel,
fixme,
# we use cause of the exception for error message output
# so some of the times we don't use `raise from` to not show the cause
# of the error
raise-missing-from,
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member,
no-else-return,
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details.
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio). You can also give a reporter class, e.g.
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages.
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit
[LOGGING]
# Format style used to check logging format string. `old` means using %
# formatting, while `new` is for `{}` formatting.
logging-format-style=old
# Logging modules to check that the string format arguments are in logging
# function parameter format.
logging-modules=logging
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package..
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=hdfs.*,pytest.lazy_fixture,logging.TRACE,logger.trace,sys.getwindowsversion
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local,Dvcfile
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=azure
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
signature-mutators=funcy.decorators.decorator,
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expected to
# not be used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore.
ignored-argument-names=_.*|^ignored_|^unused_|args|kwargs
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=79
# Maximum number of lines in a module.
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style.
#class-attribute-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_,
s3,
gs,
x, y,
x0, y0,
x1, y1,
dx, dy
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style.
#variable-rgx=
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
[DESIGN]
# Maximum number of arguments for function / method.
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in an if statement.
max-bool-expr=5
# Maximum number of branch for function / method body.
max-branches=12
# Maximum number of locals for function / method body.
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body.
max-returns=6
# Maximum number of statements in function / method body.
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=cls
[IMPORTS]
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma.
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled).
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled).
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled).
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception".
overgeneral-exceptions=Exception
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Copyright 2017-2021 Iterative, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,3 +1,3 @@
include fastentrypoints.py
include LICENSE
recursive-include requirements *.txt
recursive-include dvc/repo/plots/templates *.json
+45 -32
View File
@@ -2,37 +2,32 @@
`Website <https://dvc.org>`_
• `Docs <https://dvc.org/doc>`_
• `VS Code Extension <https://marketplace.visualstudio.com/items?itemName=Iterative.dvc>`_
• `Blog <http://blog.dataversioncontrol.com>`_
• `Twitter <https://twitter.com/DVCorg>`_
• `Chat (Community & Support) <https://dvc.org/chat>`_
• `Tutorial <https://dvc.org/doc/get-started>`_
• `Mailing List <https://sweedom.us10.list-manage.com/subscribe/post?u=a08bf93caae4063c4e6a351f6&id=24c0ecc49a>`_
|CI| |Maintainability| |Coverage| |Donate| |DOI|
|CI| |Maintainability| |Coverage| |VS Code| |DOI|
|PyPI| |Packages| |Brew| |Conda| |Choco| |Snap|
|
**Data Version Control** or **DVC** is an **open-source** tool for data science and machine
learning projects. Key features:
**Data Version Control** or **DVC** is a command line tool and `VS Code Extension <https://marketplace.visualstudio.com/items?itemName=Iterative.dvc>`_ to help you develop reproducible machine learning projects:
#. Simple **command line** Git-like experience. Does not require installing and maintaining
any databases. Does not depend on any proprietary online services.
#. **Version** your data and models. Store them in your cloud storage but keep
their version info in your Git repo.
#. Management and versioning of **datasets** and **machine learning
models**. Data can be saved in S3, Google cloud, Azure, Alibaba
cloud, SSH server, HDFS, or even local HDD RAID.
#. **Iterate** fast with lightweight pipelines. When you make changes, only run
the steps impacted by those changes.
#. Makes projects **reproducible** and **shareable**; helping to answer questions about how
a model was built.
#. **Track** experiments in your local Git repo (no servers needed).
#. Helps manage experiments with Git tags/branches and **metrics** tracking.
#. **Compare** any data, code, parameters, model, or performance plots
**DVC** aims to replace spreadsheet and document sharing tools (such as Excel or Google Docs)
frequently used as both knowledge repositories and team ledgers.
DVC also replaces both ad-hoc scripts to track, move, and deploy different model versions
and ad-hoc data file suffixes and prefixes.
#. **Share** experiments and automatically reproduce anyone's experiment.
.. contents:: **Contents**
:backlinks: none
@@ -58,7 +53,7 @@ Google Cloud, etc) or any on-premise network storage (via SSH, for example).
The DVC pipelines (computational graph) feature connects code and data together. It is possible to explicitly
specify all steps required to produce a model: input dependencies including data, commands to run,
and output information to be saved. See the quick start section below or
and output information to be saved. See the quick start sections below or
the `Get Started <https://dvc.org/doc/get-started>`_ tutorial to learn more.
Quick start
@@ -86,14 +81,27 @@ Please read `Get Started <https://dvc.org/doc/get-started>`_ guide for a full ve
| | | ``$ dvc push`` |
+-----------------------------------+----------------------------------------------------------------------------+
Visual Studio Code Extension
============================
|VS Code|
To get use DVC as a GUI right from your VS Code IDE, install the `DVC Extension <https://marketplace.visualstudio.com/items?itemName=Iterative.dvc>`_ from the Marketplace.
It currently features experiment tracking and data management, and more features (data pipeline support, etc.) are coming soon!
|VS Code Extension Overview|
Note: You'll have to install core DVC on your system separately (as detailed
below). The Extension will guide you if needed.
Installation
============
There are four options to install DVC: ``pip``, Homebrew, Conda (Anaconda) or an OS-specific package.
There are several ways to install DVC: in VS Code; using ``snap``, ``choco``, ``brew``, ``conda``, ``pip``; or with an OS-specific package.
Full instructions are `available here <https://dvc.org/doc/get-started/install>`_.
Snap (Snapcraft/Linux)
----------------------
Snapcraft (Linux)
-----------------
|Snap|
@@ -103,7 +111,7 @@ Snap (Snapcraft/Linux)
This corresponds to the latest tagged release.
Add ``--beta`` for the latest tagged release candidate,
or ``--edge`` for the latest ``master`` version.
or ``--edge`` for the latest ``main`` version.
Choco (Chocolatey/Windows)
--------------------------
@@ -169,22 +177,24 @@ Ubuntu / Debian (deb)
.. code-block:: bash
sudo wget https://dvc.org/deb/dvc.list -O /etc/apt/sources.list.d/dvc.list
sudo apt-get update
sudo apt-get install dvc
wget -qO - https://dvc.org/deb/iterative.asc | sudo apt-key add -
sudo apt update
sudo apt install dvc
Fedora / CentOS (rpm)
^^^^^^^^^^^^^^^^^^^^^
.. code-block:: bash
sudo wget https://dvc.org/rpm/dvc.repo -O /etc/yum.repos.d/dvc.repo
sudo rpm --import https://dvc.org/rpm/iterative.asc
sudo yum update
sudo yum install dvc
Comparison to related technologies
==================================
#. Data Engineering tools such as `AirFlow <https://airflow.apache.org/>`,
`Luigi <https://github.com/spotify/luigi>`, and others - in DVC data,
#. Data Engineering tools such as `AirFlow <https://airflow.apache.org/>`_,
`Luigi <https://github.com/spotify/luigi>`_, and others - in DVC data,
model and ML pipelines represent a single ML project focused on data
scientists' experience. Data engineering tools orchestrate multiple data
projects and focus on efficient execution. A DVC project can be used from
@@ -212,14 +222,14 @@ Comparison to related technologies
#. `DVC Studio <https://studio.iterative.ai/>`_ - official online
platform for DVC projects. It can be used to manage data and models, run
and track experiments, and visualize and share results. Also, it
integrates with `CML (CI/CD for ML) <https://cml.dev/>` for training
integrates with `CML (CI/CD for ML) <https://cml.dev/>`__ for training
models in the cloud or Kubernetes.
Contributing
============
|Maintainability| |Donate|
|Maintainability|
Contributions are welcome! Please see our `Contributing Guide <https://dvc.org/doc/user-guide/contributing/core>`_ for more
details. Thanks to all our contributors!
@@ -254,7 +264,10 @@ Barrak, A., Eghan, E.E. and Adams, B. `On the Co-evolution of ML Pipelines and S
:target: https://dvc.org
:alt: DVC logo
.. |CI| image:: https://github.com/iterative/dvc/workflows/Tests/badge.svg?branch=master
.. |VS Code Extension Overview| image:: https://raw.githubusercontent.com/iterative/vscode-dvc/main/extension/docs/overview.gif
:alt: DVC Extension for VS Code
.. |CI| image:: https://github.com/iterative/dvc/workflows/Tests/badge.svg?branch=main
:target: https://github.com/iterative/dvc/actions
:alt: GHA Tests
@@ -262,14 +275,10 @@ Barrak, A., Eghan, E.E. and Adams, B. `On the Co-evolution of ML Pipelines and S
:target: https://codeclimate.com/github/iterative/dvc
:alt: Code Climate
.. |Coverage| image:: https://codecov.io/gh/iterative/dvc/branch/master/graph/badge.svg
.. |Coverage| image:: https://codecov.io/gh/iterative/dvc/branch/main/graph/badge.svg
:target: https://codecov.io/gh/iterative/dvc
:alt: Codecov
.. |Donate| image:: https://img.shields.io/badge/patreon-donate-green.svg?logo=patreon
:target: https://www.patreon.com/DVCorg/overview
:alt: Donate
.. |Snap| image:: https://img.shields.io/badge/snap-install-82BEA0.svg?logo=snapcraft
:target: https://snapcraft.io/dvc
:alt: Snapcraft
@@ -305,3 +314,7 @@ Barrak, A., Eghan, E.E. and Adams, B. `On the Co-evolution of ML Pipelines and S
.. |Contribs| image:: https://contrib.rocks/image?repo=iterative/dvc
:target: https://github.com/iterative/dvc/graphs/contributors
:alt: Contributors
.. |VS Code| image:: https://vsmarketplacebadge.apphb.com/version/Iterative.dvc.svg
:target: https://marketplace.visualstudio.com/items?itemName=Iterative.dvc
:alt: VS Code Extension
+1 -1
View File
@@ -1,7 +1,7 @@
"""Main entry point for DVC command line tool."""
import sys
from dvc.main import main
from dvc.cli import main
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+72 -1
View File
@@ -1,10 +1,61 @@
from contextlib import ExitStack, contextmanager
from typing import TYPE_CHECKING
from datetime import datetime
from typing import TYPE_CHECKING, Callable, Union
if TYPE_CHECKING:
from argparse import Namespace
@contextmanager
def viztracer_profile(
path: Union[Callable[[], str], str],
max_stack_depth: int = -1,
):
try:
import viztracer # pylint: disable=import-error
except ImportError:
print("Failed to run profiler, viztracer is not installed")
yield
return
tracer = viztracer.VizTracer(max_stack_depth=max_stack_depth)
tracer.start()
yield
tracer.stop()
tracer.save(path() if callable(path) else path)
@contextmanager
def yappi_profile(
path: Union[Callable[[], str], str] = None, wall_clock: bool = True
):
try:
import yappi # pylint: disable=import-error
except ImportError:
print("Failed to run profiler, yappi is not installed")
yield
return
yappi.set_clock_type("wall" if wall_clock else "cpu")
yappi.start()
yield
yappi.stop()
# pylint:disable=no-member
if path:
stats = yappi.get_func_stats()
fpath = path() if callable(path) else path
stats.save(fpath, "callgrind")
else:
yappi.get_func_stats().print_all()
yappi.get_thread_stats().print_all()
yappi.clear_stats()
@contextmanager
def instrument(html_output=False):
"""Run a statistical profiler"""
@@ -70,6 +121,19 @@ def debugtools(args: "Namespace" = None, **kwargs):
stack.enter_context(profile(kw.get("cprofile_dump")))
if kw.get("instrument") or kw.get("instrument_open"):
stack.enter_context(instrument(kw.get("instrument_open", False)))
if kw.get("yappi"):
output = "callgrind.dvc-{0:%Y%m%d}_{0:%H%M%S}.out"
stack.enter_context(
yappi_profile(path=lambda: output.format(datetime.now()))
)
if kw.get("viztracer") or kw.get("viztracer_depth"):
output = "viztracer.dvc-{0:%Y%m%d}_{0:%H%M%S}.json"
stack.enter_context(
viztracer_profile(
path=lambda: output.format(datetime.now()),
max_stack_depth=kw.get("viztracer_depth") or -1,
)
)
yield
@@ -79,6 +143,13 @@ def add_debugging_flags(parser):
parser.add_argument(
"--cprofile", action="store_true", default=False, help=SUPPRESS
)
parser.add_argument(
"--yappi", action="store_true", default=False, help=SUPPRESS
)
parser.add_argument(
"--viztracer", action="store_true", default=False, help=SUPPRESS
)
parser.add_argument("--viztracer-depth", type=int, help=SUPPRESS)
parser.add_argument("--cprofile-dump", help=SUPPRESS)
parser.add_argument(
"--pdb", action="store_true", default=False, help=SUPPRESS
+7 -5
View File
@@ -68,7 +68,7 @@ def send(path):
url = "https://analytics.dvc.org"
headers = {"content-type": "application/json"}
with open(path) as fobj:
with open(path, encoding="utf-8") as fobj:
report = json.load(fobj)
report.update(_runtime_info())
@@ -82,10 +82,12 @@ def send(path):
def _scm_in_use():
from scmrepo.noscm import NoSCM
from dvc.exceptions import NotDvcRepoError
from dvc.repo import Repo
from dvc.scm import SCM, NoSCM
from dvc.scm.base import SCMError
from .scm import SCM, SCMError
try:
scm = SCM(root_dir=Repo.find_root())
@@ -173,13 +175,13 @@ def _find_or_create_user_id():
try:
with Lock(lockfile):
try:
with open(fname) as fobj:
with open(fname, encoding="utf-8") as fobj:
user_id = json.load(fobj)["user_id"]
except (FileNotFoundError, ValueError, KeyError):
user_id = str(uuid.uuid4())
with open(fname, "w") as fobj:
with open(fname, "w", encoding="utf-8") as fobj:
json.dump({"user_id": user_id}, fobj)
return user_id
-123
View File
@@ -1,123 +0,0 @@
import os
from contextlib import _GeneratorContextManager as GCM
from funcy import reraise
from dvc.exceptions import OutputNotFoundError, PathMissingError
from dvc.path_info import PathInfo
from dvc.repo import Repo
def get_url(path, repo=None, rev=None, remote=None):
"""
Returns the URL to the storage location of a data file or directory tracked
in a DVC repo. For Git repos, HEAD is used unless a rev argument is
supplied. The default remote is tried unless a remote argument is supplied.
Raises OutputNotFoundError if the file is not tracked by DVC.
NOTE: This function does not check for the actual existence of the file or
directory in the remote storage.
"""
with Repo.open(repo, rev=rev, subrepos=True, uninitialized=True) as _repo:
path_info = PathInfo(_repo.root_dir) / path
with reraise(FileNotFoundError, PathMissingError(path, repo)):
metadata = _repo.repo_fs.metadata(path_info)
if not metadata.is_dvc:
raise OutputNotFoundError(path, repo)
cloud = metadata.repo.cloud
md5 = metadata.repo.dvcfs.info(path_info)["md5"]
return cloud.get_url_for(remote, checksum=md5)
def open( # noqa, pylint: disable=redefined-builtin
path, repo=None, rev=None, remote=None, mode="r", encoding=None
):
"""
Open file in the supplied path tracked in a repo (both DVC projects and
plain Git repos are supported). For Git repos, HEAD is used unless a rev
argument is supplied. The default remote is tried unless a remote argument
is supplied. It may only be used as a context manager:
with dvc.api.open(
'path/to/file',
repo='https://example.com/url/to/repo'
) as fd:
# ... Handle file object fd
"""
args = (path,)
kwargs = {
"repo": repo,
"remote": remote,
"rev": rev,
"mode": mode,
"encoding": encoding,
}
return _OpenContextManager(_open, args, kwargs)
class _OpenContextManager(GCM):
def __init__(
self, func, args, kwds
): # pylint: disable=super-init-not-called
self.gen = func(*args, **kwds)
self.func, self.args, self.kwds = func, args, kwds
def __getattr__(self, name):
raise AttributeError(
"dvc.api.open() should be used in a with statement."
)
def _open(path, repo=None, rev=None, remote=None, mode="r", encoding=None):
with Repo.open(repo, rev=rev, subrepos=True, uninitialized=True) as _repo:
with _repo.open_by_relpath(
path, remote=remote, mode=mode, encoding=encoding
) as fd:
yield fd
def read(path, repo=None, rev=None, remote=None, mode="r", encoding=None):
"""
Returns the contents of a tracked file (by DVC or Git). For Git repos, HEAD
is used unless a rev argument is supplied. The default remote is tried
unless a remote argument is supplied.
"""
with open(
path, repo=repo, rev=rev, remote=remote, mode=mode, encoding=encoding
) as fd:
return fd.read()
def make_checkpoint():
"""
Signal DVC to create a checkpoint experiment.
If the current process is being run from DVC, this function will block
until DVC has finished creating the checkpoint. Otherwise, this function
will return immediately.
"""
import builtins
from time import sleep
from dvc.env import DVC_CHECKPOINT, DVC_ROOT
from dvc.stage.monitor import CheckpointTask
if os.getenv(DVC_CHECKPOINT) is None:
return
root_dir = os.getenv(DVC_ROOT, Repo.find_root())
signal_file = os.path.join(
root_dir, Repo.DVC_DIR, "tmp", CheckpointTask.SIGNAL_FILE
)
with builtins.open(signal_file, "w") as fobj:
# NOTE: force flushing/writing empty file to disk, otherwise when
# run in certain contexts (pytest) file may not actually be written
fobj.write("")
fobj.flush()
os.fsync(fobj.fileno())
while os.path.exists(signal_file):
sleep(0.1)
+9
View File
@@ -0,0 +1,9 @@
from .data import ( # noqa, pylint: disable=redefined-builtin
get_url,
open,
read,
)
from .experiments import make_checkpoint
from .params import params_show
__all__ = ["get_url", "make_checkpoint", "open", "params_show", "read"]
+213
View File
@@ -0,0 +1,213 @@
from contextlib import _GeneratorContextManager as GCM
from typing import Optional
from funcy import reraise
from dvc.exceptions import OutputNotFoundError, PathMissingError
from dvc.repo import Repo
def get_url(path, repo=None, rev=None, remote=None):
"""
Returns the URL to the storage location of a data file or directory tracked
in a DVC repo. For Git repos, HEAD is used unless a rev argument is
supplied. The default remote is tried unless a remote argument is supplied.
Raises OutputNotFoundError if the file is not tracked by DVC.
NOTE: This function does not check for the actual existence of the file or
directory in the remote storage.
"""
with Repo.open(repo, rev=rev, subrepos=True, uninitialized=True) as _repo:
fs_path = _repo.dvcfs.from_os_path(path)
with reraise(FileNotFoundError, PathMissingError(path, repo)):
info = _repo.dvcfs.info(fs_path)
dvc_info = info.get("dvc_info")
if not dvc_info:
raise OutputNotFoundError(path, repo)
dvc_repo = info["repo"]
md5 = dvc_info["md5"]
return dvc_repo.cloud.get_url_for(remote, checksum=md5)
class _OpenContextManager(GCM):
def __init__(
self, func, args, kwds
): # pylint: disable=super-init-not-called
self.gen = func(*args, **kwds)
self.func, self.args, self.kwds = func, args, kwds
def __getattr__(self, name):
raise AttributeError(
"dvc.api.open() should be used in a with statement."
)
def open( # noqa, pylint: disable=redefined-builtin
path: str,
repo: Optional[str] = None,
rev: Optional[str] = None,
remote: Optional[str] = None,
mode: str = "r",
encoding: Optional[str] = None,
):
"""
Opens a file tracked in a DVC project.
This function may only be used as a context manager (using the `with`
keyword, as shown in the examples).
This function makes a direct connection to the remote storage, so the file
contents can be streamed. Your code can process the data buffer as it's
streamed, which optimizes memory usage.
Note:
Use dvc.api.read() to load the complete file contents
in a single function call, no context manager involved.
Neither function utilizes disc space.
Args:
path (str): location and file name of the target to open,
relative to the root of `repo`.
repo (str, optional): location of the DVC project or Git Repo.
Defaults to the current DVC project (found by walking up from the
current working directory tree).
It can be a URL or a file system path.
Both HTTP and SSH protocols are supported for online Git repos
(e.g. [user@]server:project.git).
rev (str, optional): Any `Git revision`_ such as a branch or tag name,
a commit hash or a dvc experiment name.
Defaults to HEAD.
If `repo` is not a Git repo, this option is ignored.
remote (str, optional): Name of the `DVC remote`_ used to form the
returned URL string.
Defaults to the `default remote`_ of `repo`.
For local projects, the cache is tried before the default remote.
mode (str, optional): Specifies the mode in which the file is opened.
Defaults to "r" (read).
Mirrors the namesake parameter in builtin `open()`_.
Only reading `mode` is supported.
encoding(str, optional): `Codec`_ used to decode the file contents.
Defaults to None.
This should only be used in text mode.
Mirrors the namesake parameter in builtin `open()`_.
Returns:
_OpenContextManager: A context manager that generatse a corresponding
`file object`_.
The exact type of file object depends on the mode used.
For more details, please refer to Python's `open()`_ built-in,
which is used under the hood.
Raises:
AttributeError: If this method is not used as a context manager.
ValueError: If non-read `mode` is used.
Examples:
- Use data or models from a DVC repository.
Any file tracked in a DVC project (and stored remotely) can be
processed directly in your Python code with this API.
For example, an XML file tracked in a public DVC repo on GitHub can be
processed like this:
>>> from xml.sax import parse
>>> import dvc.api
>>> from mymodule import mySAXHandler
>>> with dvc.api.open(
... 'get-started/data.xml',
... repo='https://github.com/iterative/dataset-registry'
... ) as fd:
... parse(fd, mySAXHandler)
We use a SAX XML parser here because dvc.api.open() is able to stream
the data from remote storage.
The mySAXHandler object should handle the event-driven parsing of the
document in this case.
This increases the performance of the code (minimizing memory usage),
and is typically faster than loading the whole data into memory.
- Accessing private repos
This is just a matter of using the right repo argument, for example an
SSH URL (requires that the credentials are configured locally):
>>> import dvc.api
>>> with dvc.api.open(
... 'features.dat',
... repo='[email protected]:path/to/repo.git'
... ) as fd:
... # ... Process 'features'
... pass
- Use different versions of data
Any git revision (see `rev`) can be accessed programmatically.
For example, if your DVC repo has tagged releases of a CSV dataset:
>>> import csv
>>> import dvc.api
>>> with dvc.api.open(
... 'clean.csv',
... rev='v1.1.0'
... ) as fd:
... reader = csv.reader(fd)
... # ... Process 'clean' data from version 1.1.0
.. _Git revision:
https://git-scm.com/docs/revisions
.. _DVC remote:
https://dvc.org/doc/command-reference/remote
.. _default remote:
https://dvc.org/doc/command-reference/remote/default
.. _open():
https://docs.python.org/3/library/functions.html#open
.. _Codec:
https://docs.python.org/3/library/codecs.html#standard-encodings
.. _file object:
https://docs.python.org/3/glossary.html#term-file-object
"""
if "r" not in mode:
raise ValueError("Only reading `mode` is supported.")
args = (path,)
kwargs = {
"repo": repo,
"remote": remote,
"rev": rev,
"mode": mode,
"encoding": encoding,
}
return _OpenContextManager(_open, args, kwargs)
def _open(path, repo=None, rev=None, remote=None, mode="r", encoding=None):
with Repo.open(repo, rev=rev, subrepos=True, uninitialized=True) as _repo:
with _repo.open_by_relpath(
path, remote=remote, mode=mode, encoding=encoding
) as fd:
yield fd
def read(path, repo=None, rev=None, remote=None, mode="r", encoding=None):
"""
Returns the contents of a tracked file (by DVC or Git). For Git repos, HEAD
is used unless a rev argument is supplied. The default remote is tried
unless a remote argument is supplied.
"""
with open(
path, repo=repo, rev=rev, remote=remote, mode=mode, encoding=encoding
) as fd:
return fd.read()
+33
View File
@@ -0,0 +1,33 @@
import builtins
import os
from time import sleep
from dvc.env import DVC_CHECKPOINT, DVC_ROOT
from dvc.repo import Repo
from dvc.stage.monitor import CheckpointTask
def make_checkpoint():
"""
Signal DVC to create a checkpoint experiment.
If the current process is being run from DVC, this function will block
until DVC has finished creating the checkpoint. Otherwise, this function
will return immediately.
"""
if os.getenv(DVC_CHECKPOINT) is None:
return
root_dir = os.getenv(DVC_ROOT, Repo.find_root())
signal_file = os.path.join(
root_dir, Repo.DVC_DIR, "tmp", CheckpointTask.SIGNAL_FILE
)
with builtins.open(signal_file, "w", encoding="utf-8") as fobj:
# NOTE: force flushing/writing empty file to disk, otherwise when
# run in certain contexts (pytest) file may not actually be written
fobj.write("")
fobj.flush()
os.fsync(fobj.fileno())
while os.path.exists(signal_file):
sleep(0.1)
+267
View File
@@ -0,0 +1,267 @@
from collections import Counter
from typing import Dict, Iterable, Optional, Union
from funcy import first
from dvc.repo import Repo
def params_show(
*targets: str,
repo: Optional[str] = None,
stages: Optional[Union[str, Iterable[str]]] = None,
rev: Optional[str] = None,
deps: bool = False,
) -> Dict:
"""Get parameters tracked in `repo`.
Without arguments, this function will retrieve all params from all tracked
parameter files, for the current working tree.
See the options below to restrict the parameters retrieved.
Args:
*targets (str, optional): Names of the parameter files to retrieve
params from. For example, "params.py, myparams.toml".
If no `targets` are provided, all parameter files tracked in `dvc.yaml`
will be used.
Note that targets don't necessarily have to be defined in `dvc.yaml`.
repo (str, optional): location of the DVC repository.
Defaults to the current project (found by walking up from the
current working directory tree).
It can be a URL or a file system path.
Both HTTP and SSH protocols are supported for online Git repos
(e.g. [user@]server:project.git).
stages (Union[str, Iterable[str]], optional): Name or names of the
stages to retrieve parameters from.
Defaults to `None`.
If `None`, all parameters from all stages will be retrieved.
rev (str, optional): Name of the `Git revision`_ to retrieve parameters
from.
Defaults to `None`.
An example of git revision can be a branch or tag name, a commit
hash or a dvc experiment name.
If `repo` is not a Git repo, this option is ignored.
If `None`, the current working tree will be used.
deps (bool, optional): Whether to retrieve only parameters that are
stage dependencies or not.
Defaults to `False`.
Returns:
Dict: See Examples below.
Examples:
- No arguments.
Working on https://github.com/iterative/example-get-started
>>> import json
>>> import dvc.api
>>> params = dvc.api.params_show()
>>> print(json.dumps(params, indent=4))
{
"prepare": {
"split": 0.2,
"seed": 20170428
},
"featurize": {
"max_features": 200,
"ngrams": 2
},
"train": {
"seed": 20170428,
"n_est": 50,
"min_split": 0.01
}
}
---
- Filtering with `stages`.
Working on https://github.com/iterative/example-get-started
`stages` can a single string:
>>> import json
>>> import dvc.api
>>> params = dvc.api.params_show(stages="prepare")
>>> print(json.dumps(params, indent=4))
{
"prepare": {
"split": 0.2,
"seed": 20170428
}
}
Or an iterable of strings:
>>> import json
>>> import dvc.api
>>> params = dvc.api.params_show(stages=["prepare", "train"])
>>> print(json.dumps(params, indent=4))
{
"prepare": {
"split": 0.2,
"seed": 20170428
},
"train": {
"seed": 20170428,
"n_est": 50,
"min_split": 0.01
}
}
---
- Using `rev`.
Working on https://github.com/iterative/example-get-started
>>> import json
>>> import dvc.api
>>> params = dvc.api.params_show(rev="tune-hyperparams")
>>> print(json.dumps(params, indent=4))
{
"prepare": {
"split": 0.2,
"seed": 20170428
},
"featurize": {
"max_features": 200,
"ngrams": 2
},
"train": {
"seed": 20170428,
"n_est": 100,
"min_split": 8
}
}
---
- Using `targets`.
Working on `multi-params-files` folder of
https://github.com/iterative/pipeline-conifguration
You can pass a single target:
>>> import json
>>> import dvc.api
>>> params = dvc.api.params_show("params.yaml")
>>> print(json.dumps(params, indent=4))
{
"run_mode": "prod",
"configs": {
"dev": "configs/params_dev.yaml",
"test": "configs/params_test.yaml",
"prod": "configs/params_prod.yaml"
},
"evaluate": {
"dataset": "micro",
"size": 5000,
"metrics": ["f1", "roc-auc"],
"metrics_file": "reports/metrics.json",
"plots_cm": "reports/plot_confusion_matrix.png"
}
}
Or multiple targets:
>>> import json
>>> import dvc.api
>>> params = dvc.api.params_show(
... "configs/params_dev.yaml", "configs/params_prod.yaml")
>>> print(json.dumps(params, indent=4))
{
"configs/params_prod.yaml:run_mode": "prod",
"configs/params_prod.yaml:config_file": "configs/params_prod.yaml",
"configs/params_prod.yaml:data_load": {
"dataset": "large",
"sampling": {
"enable": true,
"size": 50000
}
},
"configs/params_prod.yaml:train": {
"epochs": 1000
},
"configs/params_dev.yaml:run_mode": "dev",
"configs/params_dev.yaml:config_file": "configs/params_dev.yaml",
"configs/params_dev.yaml:data_load": {
"dataset": "development",
"sampling": {
"enable": true,
"size": 1000
}
},
"configs/params_dev.yaml:train": {
"epochs": 10
}
}
---
- Git URL as `repo`.
>>> import json
>>> import dvc.api
>>> params = dvc.api.params_show(
... repo="https://github.com/iterative/demo-fashion-mnist")
{
"train": {
"batch_size": 128,
"hidden_units": 64,
"dropout": 0.4,
"num_epochs": 10,
"lr": 0.001,
"conv_activation": "relu"
}
}
.. _Git revision:
https://git-scm.com/docs/revisions
"""
if isinstance(stages, str):
stages = [stages]
def _onerror_raise(result: Dict, exception: Exception, *args, **kwargs):
raise exception
def _postprocess(params):
processed = {}
for rev, rev_data in params.items():
processed[rev] = {}
counts = Counter()
for file_data in rev_data["data"].values():
for k in file_data["data"]:
counts[k] += 1
for file_name, file_data in rev_data["data"].items():
to_merge = {
(k if counts[k] == 1 else f"{file_name}:{k}"): v
for k, v in file_data["data"].items()
}
processed[rev] = {**processed[rev], **to_merge}
if "workspace" in processed:
del processed["workspace"]
return processed[first(processed)]
with Repo.open(repo) as _repo:
params = _repo.params.show(
revs=rev if rev is None else [rev],
targets=targets,
deps=deps,
onerror=_onerror_raise,
stages=stages,
)
return _postprocess(params)
-351
View File
@@ -1,351 +0,0 @@
import logging
from shortuuid import uuid
from dvc import prompt
from dvc.exceptions import (
CacheLinkError,
CheckoutError,
ConfirmRemoveError,
DvcException,
)
from dvc.ignore import DvcIgnoreFilter
from dvc.objects import check, load
from dvc.objects.db.slow_link_detection import ( # type: ignore[attr-defined]
slow_link_guard,
)
from dvc.objects.errors import ObjectFormatError
from dvc.objects.stage import stage
from dvc.types import Optional
logger = logging.getLogger(__name__)
def _changed(path_info, fs, obj, cache, state=None):
logger.trace("checking if '%s'('%s') has changed.", path_info, obj)
try:
check(cache, obj)
except (FileNotFoundError, ObjectFormatError):
logger.debug(
"cache for '%s'('%s') has changed.", path_info, obj.hash_info
)
return True
actual = state.get(path_info, fs) if state else None
if actual is None:
try:
actual = stage(
cache, path_info, fs, obj.hash_info.name, dry_run=True
)[1].hash_info
except FileNotFoundError:
logger.debug("'%s' doesn't exist.", path_info)
return True
if obj.hash_info != actual:
logger.debug(
"hash value '%s' for '%s' has changed (actual '%s').",
obj.hash_info,
actual,
path_info,
)
return True
logger.trace("'%s' hasn't changed.", path_info)
return False
def _remove(path_info, fs, cache, force=False):
if not fs.exists(path_info):
return
if force:
fs.remove(path_info)
return
current = stage(cache, path_info, fs, fs.PARAM_CHECKSUM, dry_run=True)[
1
].hash_info
try:
obj = load(cache, current)
check(cache, obj)
except (FileNotFoundError, ObjectFormatError):
msg = (
f"file/directory '{path_info}' is going to be removed. "
"Are you sure you want to proceed?"
)
if not prompt.confirm(msg):
raise ConfirmRemoveError(str(path_info))
fs.remove(path_info)
def _verify_link(cache, path_info, link_type):
if link_type == "hardlink" and cache.fs.getsize(path_info) == 0:
return
if cache.cache_type_confirmed:
return
is_link = getattr(cache.fs, f"is_{link_type}", None)
if is_link and not is_link(path_info):
cache.fs.remove(path_info)
raise DvcException(f"failed to verify {link_type}")
cache.cache_type_confirmed = True
def _do_link(cache, from_info, to_info, link_method):
if cache.fs.exists(to_info):
raise DvcException(f"Link '{to_info}' already exists!")
link_method(from_info, to_info)
logger.debug(
"Created '%s': %s -> %s", cache.cache_types[0], from_info, to_info
)
@slow_link_guard
def _try_links(cache, from_info, to_info, link_types):
while link_types:
link_method = getattr(cache.fs, link_types[0])
try:
_do_link(cache, from_info, to_info, link_method)
_verify_link(cache, to_info, link_types[0])
return
except DvcException as exc:
logger.debug(
"Cache type '%s' is not supported: %s", link_types[0], exc
)
del link_types[0]
raise CacheLinkError([to_info])
def _link(cache, from_info, to_info):
cache.makedirs(to_info.parent)
try:
_try_links(cache, from_info, to_info, cache.cache_types)
except FileNotFoundError as exc:
raise CheckoutError([to_info]) from exc
def _cache_is_copy(cache, path_info):
"""Checks whether cache uses copies."""
if cache.cache_type_confirmed:
return cache.cache_types[0] == "copy"
if set(cache.cache_types) <= {"copy"}:
return True
workspace_file = path_info.with_name("." + uuid())
test_cache_file = cache.path_info / ".cache_type_test_file"
if not cache.fs.exists(test_cache_file):
cache.makedirs(test_cache_file.parent)
with cache.fs.open(test_cache_file, "wb") as fobj:
fobj.write(bytes(1))
try:
_link(cache, test_cache_file, workspace_file)
finally:
cache.fs.remove(workspace_file)
cache.fs.remove(test_cache_file)
cache.cache_type_confirmed = True
return cache.cache_types[0] == "copy"
def _checkout_file(
path_info,
fs,
obj,
cache,
force,
progress_callback=None,
relink=False,
state=None,
):
"""The file is changed we need to checkout a new copy"""
modified = False
cache_info = cache.hash_to_path_info(obj.hash_info.value)
if fs.exists(path_info):
if not relink and _changed(path_info, fs, obj, cache, state=state):
modified = True
_remove(path_info, fs, cache, force=force)
_link(cache, cache_info, path_info)
else:
if fs.iscopy(path_info) and _cache_is_copy(cache, path_info):
cache.unprotect(path_info)
else:
_remove(path_info, fs, cache, force=force)
_link(cache, cache_info, path_info)
else:
_link(cache, cache_info, path_info)
modified = True
if state:
state.save(path_info, fs, obj.hash_info)
if progress_callback:
progress_callback(str(path_info))
return modified
def _remove_redundant_files(
path_info, fs, obj, cache, force, dvcignore: Optional[DvcIgnoreFilter]
):
if dvcignore:
existing_files = set(dvcignore.walk_files(fs, path_info))
else:
existing_files = set(fs.walk_files(path_info))
needed_files = {path_info.joinpath(*key) for key, _ in obj}
redundant_files = existing_files - needed_files
for path in redundant_files:
_remove(path, fs, cache, force)
return bool(redundant_files)
def _checkout_dir(
path_info,
fs,
obj,
cache,
force,
progress_callback=None,
relink=False,
dvcignore: Optional[DvcIgnoreFilter] = None,
state=None,
):
modified = False
# Create dir separately so that dir is created
# even if there are no files in it
if not fs.exists(path_info):
modified = True
fs.makedirs(path_info)
logger.debug("Linking directory '%s'.", path_info)
failed = []
for entry_key, entry_obj in obj:
try:
entry_modified = _checkout_file(
path_info.joinpath(*entry_key),
fs,
entry_obj,
cache,
force,
progress_callback,
relink,
state=None,
)
if entry_modified:
modified = True
except CheckoutError as exc:
failed.extend(exc.target_infos)
modified = (
_remove_redundant_files(
path_info, fs, obj, cache, force, dvcignore=dvcignore
)
or modified
)
if failed:
raise CheckoutError(failed)
if state:
state.save(path_info, fs, obj.hash_info)
# relink is not modified, assume it as nochange
return modified and not relink
def _checkout(
path_info,
fs,
obj,
cache,
force=False,
progress_callback=None,
relink=False,
dvcignore: Optional[DvcIgnoreFilter] = None,
state=None,
):
if not obj.hash_info.isdir:
ret = _checkout_file(
path_info, fs, obj, cache, force, progress_callback, relink, state
)
else:
ret = _checkout_dir(
path_info,
fs,
obj,
cache,
force,
progress_callback,
relink,
dvcignore=dvcignore,
state=state,
)
if state:
state.save_link(path_info, fs)
return ret
def checkout(
path_info,
fs,
obj,
cache,
force=False,
progress_callback=None,
relink=False,
quiet=False,
dvcignore: Optional[DvcIgnoreFilter] = None,
state=None,
):
if path_info.scheme not in ["local", cache.fs.scheme]:
raise NotImplementedError
failed = None
skip = False
if not obj:
if not quiet:
logger.warning(
"No file hash info found for '%s'. It won't be created.",
path_info,
)
_remove(path_info, fs, cache, force=force)
failed = path_info
elif not relink and not _changed(path_info, fs, obj, cache, state=state):
logger.trace("Data '%s' didn't change.", path_info) # type: ignore
skip = True
if failed or skip:
if progress_callback and obj:
progress_callback(str(path_info), len(obj))
if failed:
raise CheckoutError([failed])
return
logger.debug("Checking out '%s' with cache '%s'.", path_info, obj)
return _checkout(
path_info,
fs,
obj,
cache,
force,
progress_callback,
relink,
dvcignore=dvcignore,
state=state,
)
+218
View File
@@ -0,0 +1,218 @@
"""This module provides an entrypoint to the dvc cli and parsing utils."""
import logging
import sys
# Workaround for CPython bug. See [1] and [2] for more info.
# [1] https://github.com/aws/aws-cli/blob/1.16.277/awscli/clidriver.py#L55
# [2] https://bugs.python.org/issue29288
from typing import Optional
"".encode("idna")
logger = logging.getLogger("dvc")
class DvcParserError(Exception):
"""Base class for CLI parser errors."""
def __init__(self):
super().__init__("parser error")
def parse_args(argv=None):
"""Parses CLI arguments.
Args:
argv: optional list of arguments to parse. sys.argv is used by default.
Raises:
DvcParserError: raised for argument parsing errors.
"""
from .parser import get_main_parser
parser = get_main_parser()
args = parser.parse_args(argv)
args.parser = parser
return args
def _log_unknown_exceptions() -> None:
from dvc.info import get_dvc_info
from dvc.logger import FOOTER
logger.exception("unexpected error")
if logger.isEnabledFor(logging.DEBUG):
logger.debug("Version info for developers:\n%s", get_dvc_info())
logger.info(FOOTER)
return None
def _log_exceptions(exc: Exception) -> Optional[int]:
"""Try to log some known exceptions, that are not DVCExceptions."""
from dvc.utils import error_link, format_link
if isinstance(exc, OSError):
import errno
if exc.errno == errno.EMFILE:
logger.exception(
"too many open files, please visit "
"{} to see how to handle this "
"problem".format(error_link("many-files")),
extra={"tb_only": True},
)
else:
_log_unknown_exceptions()
return None
from dvc.fs import AuthError, ConfigError, RemoteMissingDepsError
if isinstance(exc, RemoteMissingDepsError):
from dvc.utils.pkg import PKG
proto = exc.protocol
by_pkg = {
"pip": f"pip install 'dvc[{proto}]'",
"conda": f"conda install -c conda-forge dvc-{proto}",
}
cmd = by_pkg.get(PKG)
if cmd:
link = format_link("https://dvc.org/doc/install")
hint = (
f"To install dvc with those dependencies, run:\n"
"\n"
f"\t{cmd}\n"
"\n"
f"See {link} for more info."
)
else:
link = format_link("https://github.com/iterative/dvc/issues")
hint = f"\nPlease report this bug to {link}. Thank you!"
logger.exception(
f"URL '{exc.url}' is supported but requires these missing "
f"dependencies: {exc.missing_deps}. {hint}",
extra={"tb_only": True},
)
return None
if isinstance(exc, (AuthError, ConfigError)):
link = format_link("https://man.dvc.org/remote/modify")
logger.exception("configuration error")
logger.exception(
f"{exc!s}\nLearn more about configuration settings at {link}.",
extra={"tb_only": True},
)
return 251
from dvc_data.hashfile.cache import DiskError
if isinstance(exc, DiskError):
from dvc.utils import relpath
directory = relpath(exc.directory)
logger.exception(
f"Could not open pickled '{exc.type}' cache.\n"
f"Remove the '{directory}' directory and then retry this command."
f"\nSee {error_link('pickle')} for more information.",
extra={"tb_only": True},
)
return None
from dvc_data.stage import IgnoreInCollectedDirError
if isinstance(exc, IgnoreInCollectedDirError):
logger.exception("")
return None
_log_unknown_exceptions()
return None
def main(argv=None): # noqa: C901
"""Main entry point for dvc CLI.
Args:
argv: optional list of arguments to parse. sys.argv is used by default.
Returns:
int: command's return code.
"""
from dvc._debug import debugtools
from dvc.config import ConfigError
from dvc.exceptions import DvcException, NotDvcRepoError
from dvc.logger import disable_other_loggers, set_loggers_level
# NOTE: stderr/stdout may be closed if we are running from dvc.daemon.
# On Linux we directly call cli.main after double forking and closing
# the copied parent's standard file descriptors. If we make any logging
# calls in this state it will cause an exception due to writing to a closed
# file descriptor.
if sys.stderr.closed: # pylint: disable=using-constant-test
logging.disable()
elif sys.stdout.closed: # pylint: disable=using-constant-test
logging.disable(logging.INFO)
args = None
disable_other_loggers()
outerLogLevel = logger.level
try:
args = parse_args(argv)
level = None
if args.quiet:
level = logging.CRITICAL
elif args.verbose == 1:
level = logging.DEBUG
elif args.verbose > 1:
level = logging.TRACE
if level is not None:
set_loggers_level(level)
logger.trace(args)
if not sys.stdout.closed and not args.quiet:
from dvc.ui import ui
ui.enable()
with debugtools(args):
cmd = args.func(args)
ret = cmd.do_run()
except ConfigError:
logger.exception("configuration error")
ret = 251
except KeyboardInterrupt:
logger.exception("interrupted by the user")
ret = 252
except NotDvcRepoError:
logger.exception("")
ret = 253
except DvcException:
ret = 255
logger.exception("")
except DvcParserError:
ret = 254
except Exception as exc: # noqa, pylint: disable=broad-except
# pylint: disable=no-member
ret = _log_exceptions(exc) or 255
try:
from dvc import analytics
if analytics.is_enabled():
analytics.collect_and_send_report(args, ret)
return ret
finally:
logger.setLevel(outerLogLevel)
from dvc.external_repo import clean_repos
# Remove cached repos in the end of the call, these are anonymous
# so won't be reused by any other subsequent run anyway.
clean_repos()
@@ -5,29 +5,6 @@ from abc import ABC, abstractmethod
logger = logging.getLogger(__name__)
def fix_subparsers(subparsers):
"""Workaround for bug in Python 3. See more info at:
https://bugs.python.org/issue16308
https://github.com/iterative/dvc/issues/769
Args:
subparsers: subparsers to fix.
"""
subparsers.required = True
subparsers.dest = "cmd"
def append_doc_link(help_message, path):
from dvc.utils import format_link
if not path:
return help_message
doc_base = "https://man.dvc.org/"
return "{message}\nDocumentation: {link}".format(
message=help_message, link=format_link(doc_base + path)
)
class CmdBase(ABC):
UNINITIALIZED = False
@@ -1,17 +1,6 @@
import argparse
import logging
import shtab
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.ui import ui
logger = logging.getLogger(__name__)
FILE = shtab.FILE
DIR = shtab.DIRECTORY
PREAMBLE = {
"bash": """
BASH_PREAMBLE = """
# $1=COMP_WORDS[1]
_dvc_compgen_DVCFiles() {
compgen -d -S '/' -- $1 # recurse into subdirs
@@ -30,11 +19,12 @@ _dvc_compgen_stages_and_files() {
}
_dvc_compgen_exps() {
local _dvc_exps=($(dvc exp list -q --all --names-only))
local _dvc_exps=($(dvc exp list -q --all-commits --names-only))
compgen -W "${_dvc_exps[*]}" -- $1
}
""",
"zsh": """
"""
ZSH_PREAMBLE = """
_dvc_compadd_DVCFiles() {
_files -g '(*?.dvc|Dvcfile|dvc.yaml)'
}
@@ -63,49 +53,21 @@ _dvc_compadd_stages_and_files() {
}
_dvc_compadd_exps() {
_describe 'experiments' "($(dvc exp list -q -a --names-only))"
_describe 'experiments' "($(dvc exp list -q --all-commits --names-only))"
}
""",
"""
PREAMBLE = {
"bash": BASH_PREAMBLE,
"zsh": ZSH_PREAMBLE,
}
FILE = shtab.FILE
DIR = shtab.DIRECTORY
DVC_FILE = {"bash": "_dvc_compgen_DVCFiles", "zsh": "_dvc_compadd_DVCFiles"}
STAGE = {"bash": "_dvc_compgen_stages", "zsh": "_dvc_compadd_stages"}
DVCFILES_AND_STAGE = {
"bash": "_dvc_compgen_stages_and_files",
"zsh": "_dvc_compadd_stages_and_files",
}
EXPERIMENT = {"bash": "_dvc_compgen_exps", "zsh": "_dvc_compadd_exps"}
class CmdCompletion(CmdBaseNoRepo):
def run(self):
from dvc.cli import get_main_parser
parser = get_main_parser()
shell = self.args.shell
script = shtab.complete(parser, shell=shell, preamble=PREAMBLE)
ui.write(script, force=True)
return 0
def add_parser(subparsers, parent_parser):
COMPLETION_HELP = "Generate shell tab completion."
COMPLETION_DESCRIPTION = "Prints out shell tab completion scripts."
completion_parser = subparsers.add_parser(
"completion",
parents=[parent_parser],
description=append_doc_link(COMPLETION_DESCRIPTION, "completion"),
help=COMPLETION_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
completion_parser.add_argument(
"-s",
"--shell",
help="Shell syntax for completions.",
default="bash",
choices=["bash", "zsh"],
)
completion_parser.set_defaults(func=CmdCompletion)
+9 -22
View File
@@ -1,11 +1,10 @@
"""DVC command line interface"""
"""Main parser for the dvc cli."""
import argparse
import logging
import os
import sys
from ._debug import add_debugging_flags
from .command import (
from dvc.commands import (
add,
cache,
check_ignore,
@@ -28,7 +27,6 @@ from .command import (
imp_url,
init,
install,
live,
ls,
machine,
metrics,
@@ -45,8 +43,8 @@ from .command import (
update,
version,
)
from .command.base import fix_subparsers
from .exceptions import DvcParserError
from . import DvcParserError
logger = logging.getLogger(__name__)
@@ -87,7 +85,6 @@ COMMANDS = [
stage,
experiments,
check_ignore,
live,
machine,
]
@@ -96,7 +93,7 @@ def _find_parser(parser, cmd_cls):
defaults = parser._defaults # pylint: disable=protected-access
if not cmd_cls or cmd_cls == defaults.get("func"):
parser.print_help()
raise DvcParserError()
raise DvcParserError
actions = parser._actions # pylint: disable=protected-access
for action in actions:
@@ -143,6 +140,8 @@ def get_parent_parser():
When overwriting `-q` or `-v`, you need to instantiate a new object
in order to prevent some weird behavior.
"""
from dvc._debug import add_debugging_flags
parent_parser = argparse.ArgumentParser(add_help=False)
add_debugging_flags(parent_parser)
log_level_group = parent_parser.add_mutually_exclusive_group()
@@ -207,23 +206,11 @@ def get_main_parser():
help="Use `dvc COMMAND --help` for command-specific help.",
)
from .utils import fix_subparsers
fix_subparsers(subparsers)
for cmd in COMMANDS:
cmd.add_parser(subparsers, parent_parser)
return parser
def parse_args(argv=None):
"""Parses CLI arguments.
Args:
argv: optional list of arguments to parse. sys.argv is used by default.
Raises:
dvc.exceptions.DvcParserError: raised for argument parsing errors.
"""
parser = get_main_parser()
args = parser.parse_args(argv)
return args
+31
View File
@@ -0,0 +1,31 @@
def fix_subparsers(subparsers):
"""Workaround for bug in Python 3. See more info at:
https://bugs.python.org/issue16308
https://github.com/iterative/dvc/issues/769
Args:
subparsers: subparsers to fix.
"""
subparsers.required = True
subparsers.dest = "cmd"
def append_doc_link(help_message, path):
from dvc.utils import format_link
if not path:
return help_message
doc_base = "https://man.dvc.org/"
return "{message}\nDocumentation: {link}".format(
message=help_message, link=format_link(doc_base + path)
)
def fix_plumbing_subparsers(subparsers):
# metavar needs to be explicitly set in order to hide plumbing subcommands
# from the 'positional arguments' choices list
# see: https://bugs.python.org/issue22848
cmds = [
cmd for cmd, parser in subparsers.choices.items() if parser.add_help
]
subparsers.metavar = "{{{}}}".format(",".join(cmds))
File diff suppressed because it is too large Load Diff
-93
View File
@@ -1,93 +0,0 @@
import argparse
from pathlib import Path
from dvc.command import completion
from dvc.command.base import CmdBase, fix_subparsers
from dvc.ui import ui
class CmdLive(CmdBase):
UNINITIALIZED = True
def _run(self, target, revs=None):
metrics, plots = self.repo.live.show(target=target, revs=revs)
if plots:
html_path = Path.cwd() / (self.args.target + "_html")
from dvc.render.utils import render
index_path = render(self.repo, plots, metrics, html_path)
ui.write(index_path.as_uri())
return 0
return 1
class CmdLiveShow(CmdLive):
def run(self):
return self._run(self.args.target)
class CmdLiveDiff(CmdLive):
def run(self):
return self._run(self.args.target, self.args.revs)
def shared_parent_parser():
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument(
"target", help="Logs dir to produce summary from"
).complete = completion.DIR
parent_parser.add_argument(
"-o",
"--out",
default=None,
help="Destination path to save plots to",
metavar="<path>",
).complete = completion.DIR
return parent_parser
def add_parser(subparsers, parent_parser):
LIVE_DESCRIPTION = (
"Commands to visualize and compare dvclive-produced logs."
)
live_parser = subparsers.add_parser(
"live",
parents=[parent_parser],
formatter_class=argparse.RawDescriptionHelpFormatter,
description=LIVE_DESCRIPTION,
)
live_subparsers = live_parser.add_subparsers(
dest="cmd",
help="Use `dvc live CMD --help` to display command-specific help.",
)
fix_subparsers(live_subparsers)
SHOW_HELP = "Visualize dvclive directory content."
live_show_parser = live_subparsers.add_parser(
"show",
parents=[parent_parser, shared_parent_parser()],
help=SHOW_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
live_show_parser.set_defaults(func=CmdLiveShow)
DIFF_HELP = (
"Show multiple versions of dvclive data, "
"by plotting it in single view."
)
live_diff_parser = live_subparsers.add_parser(
"diff",
parents=[parent_parser, shared_parent_parser()],
help=DIFF_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
live_diff_parser.add_argument(
"--revs",
nargs="*",
default=None,
help="Git revision (e.g. SHA, branch, tag)",
metavar="<commit>",
)
live_diff_parser.set_defaults(func=CmdLiveDiff)
+6 -4
View File
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
logger = logging.getLogger(__name__)
@@ -101,9 +102,10 @@ def add_parser(subparsers, parent_parser):
"--jobs",
type=int,
help=(
"Number of jobs to run simultaneously. "
"Only used along with '--to-remote'. "
"Number of jobs to run simultaneously "
"when pushing data to remote."
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
@@ -1,8 +1,8 @@
import argparse
from dvc.command import completion
from dvc.command.base import append_doc_link, fix_subparsers
from dvc.command.config import CmdConfig
from dvc.cli import completion
from dvc.cli.utils import append_doc_link, fix_subparsers
from dvc.commands.config import CmdConfig
from dvc.ui import ui
@@ -28,7 +28,7 @@ class CmdCacheDir(CmdConfig):
def add_parser(subparsers, parent_parser):
from dvc.command.config import parent_config_parser
from dvc.commands.config import parent_config_parser
CACHE_HELP = "Manage cache settings."
@@ -1,7 +1,8 @@
import argparse
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
@@ -1,8 +1,9 @@
import argparse
import operator
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import CheckoutError
from dvc.ui import ui
@@ -54,6 +55,10 @@ class CmdCheckout(CmdBase):
if exc:
raise exc
if self.args.relink:
msg = "Relinked successfully"
ui.write(msg)
return 0
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
logger = logging.getLogger(__name__)
+40
View File
@@ -0,0 +1,40 @@
import argparse
import logging
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.completion import PREAMBLE
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
logger = logging.getLogger(__name__)
class CmdCompletion(CmdBaseNoRepo):
def run(self):
import shtab
shell = self.args.shell
parser = self.args.parser
script = shtab.complete(parser, shell=shell, preamble=PREAMBLE)
ui.write(script, force=True)
return 0
def add_parser(subparsers, parent_parser):
COMPLETION_HELP = "Generate shell tab completion."
COMPLETION_DESCRIPTION = "Prints out shell tab completion scripts."
completion_parser = subparsers.add_parser(
"completion",
parents=[parent_parser],
description=append_doc_link(COMPLETION_DESCRIPTION, "completion"),
help=COMPLETION_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
completion_parser.add_argument(
"-s",
"--shell",
help="Shell syntax for completions.",
default="bash",
choices=["bash", "zsh"],
)
completion_parser.set_defaults(func=CmdCompletion)
@@ -2,7 +2,8 @@ import argparse
import logging
import os
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
logger = logging.getLogger(__name__)
@@ -1,5 +1,6 @@
from dvc.command import completion
from dvc.command.base import CmdBaseNoRepo, fix_subparsers
from dvc.cli import completion
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import fix_subparsers
class CmdDaemonBase(CmdBaseNoRepo):
+70 -10
View File
@@ -1,10 +1,15 @@
import argparse
from typing import TYPE_CHECKING
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
if TYPE_CHECKING:
from networkx import DiGraph
def _show_ascii(G):
def _show_ascii(G: "DiGraph"):
from dvc.dagascii import draw
from dvc.repo.graph import get_pipelines
@@ -17,16 +22,54 @@ def _show_ascii(G):
return "\n".join(ret)
def _show_dot(G):
def _quote_label(node):
label = str(node)
# Node names should not contain ":" unless they are quoted with "".
# See: https://github.com/pydot/pydot/issues/258.
if label[0] != '"' and label[-1] != '"':
return f'"{label}"'
return label
def _show_dot(G: "DiGraph"):
import io
import networkx as nx
from networkx.drawing.nx_pydot import write_dot
dot_file = io.StringIO()
write_dot(G, dot_file)
nx.relabel_nodes(G, _quote_label, copy=False)
write_dot(G.reverse(), dot_file)
return dot_file.getvalue()
def _show_mermaid(G, markdown: bool = False):
from dvc.repo.graph import get_pipelines
pipelines = get_pipelines(G)
graph = "flowchart TD"
total_nodes = 0
for pipeline in pipelines:
node_ids = {}
nodes = sorted(str(x) for x in pipeline.nodes)
for node in nodes:
total_nodes += 1
node_id = f"node{total_nodes}"
graph += f'\n\t{node_id}["{node}"]'
node_ids[node] = node_id
edges = sorted((str(a), str(b)) for b, a in pipeline.edges)
for a, b in edges:
graph += f"\n\t{node_ids[str(a)]}-->{node_ids[str(b)]}"
if markdown:
return f"```mermaid\n{graph}\n```"
return graph
def _collect_targets(repo, target, outs):
if not target:
return []
@@ -38,12 +81,14 @@ def _collect_targets(repo, target, outs):
targets = []
outs_trie = repo.index.outs_trie
for stage, info in pairs:
if not info:
for stage, path in pairs:
if not path:
targets.extend([str(out) for out in stage.outs])
continue
for out in outs_trie.itervalues(prefix=info.parts): # noqa: B301
for out in outs_trie.itervalues( # noqa: B301
prefix=repo.fs.path.parts(path)
):
targets.extend(str(out))
return targets
@@ -102,10 +147,11 @@ class CmdDAG(CmdBase):
if self.args.dot:
ui.write(_show_dot(G))
elif self.args.mermaid or self.args.markdown:
ui.write(_show_mermaid(G, self.args.markdown))
else:
from dvc.utils.pager import pager
pager(_show_ascii(G))
with ui.pager():
ui.write(_show_ascii(G))
return 0
@@ -125,6 +171,20 @@ def add_parser(subparsers, parent_parser):
default=False,
help="Print DAG with .dot format.",
)
dag_parser.add_argument(
"--mermaid",
action="store_true",
default=False,
help="Print DAG with mermaid format.",
)
dag_parser.add_argument(
"--md",
"--show-md",
action="store_true",
default=False,
dest="markdown",
help="Print DAG with mermaid format wrapped in Markdown block.",
)
dag_parser.add_argument(
"--full",
action="store_true",
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
logger = logging.getLogger(__name__)
@@ -18,7 +19,7 @@ class CmdDataBase(CmdBase):
class CmdDataPull(CmdDataBase):
def log_summary(self, stats):
from dvc.command.checkout import log_changes
from dvc.commands.checkout import log_changes
log_changes(stats)
super().log_summary(stats)
@@ -97,7 +98,7 @@ class CmdDataFetch(CmdDataBase):
def shared_parent_parser():
from dvc.cli import get_parent_parser
from dvc.cli.parser import get_parent_parser
# Parent parser used in pull/push/status
parent_parser = argparse.ArgumentParser(
@@ -110,7 +111,6 @@ def shared_parent_parser():
help=(
"Number of jobs to run simultaneously. "
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
@@ -127,7 +127,7 @@ def shared_parent_parser():
def add_parser(subparsers, _parent_parser):
from dvc.command.status import CmdDataStatus
from dvc.commands.status import CmdDataStatus
# Pull
PULL_HELP = "Download tracked files or directories from remote storage."
@@ -393,6 +393,7 @@ def add_parser(subparsers, _parent_parser):
help="Show status of all stages in the specified directory.",
)
status_parser.add_argument(
"--json",
"--show-json",
action="store_true",
default=False,
@@ -1,7 +1,8 @@
import argparse
import logging
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
logger = logging.getLogger(__name__)
+12 -9
View File
@@ -1,10 +1,10 @@
import argparse
import json
import logging
import os
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
logger = logging.getLogger(__name__)
@@ -16,7 +16,7 @@ def _digest(checksum):
return "{}..{}".format(checksum["old"][0:8], checksum["new"][0:8])
def _show_md(diff, show_hash=False, hide_missing=False):
def _show_markdown(diff, show_hash=False, hide_missing=False):
headers = ["Status", "Hash", "Path"] if show_hash else ["Status", "Path"]
rows = []
statuses = ["added", "deleted", "renamed", "modified"]
@@ -128,7 +128,7 @@ class CmdDiff(CmdBase):
show_hash = self.args.show_hash
hide_missing = self.args.b_rev or self.args.hide_missing
if hide_missing:
del diff["not in cache"]
diff.pop("not in cache", None)
for key, entries in diff.items():
entries = sorted(
@@ -142,10 +142,10 @@ class CmdDiff(CmdBase):
del entry["hash"]
diff[key] = entries
if self.args.show_json:
ui.write(json.dumps(diff))
elif self.args.show_md:
_show_md(diff, show_hash, hide_missing)
if self.args.json:
ui.write_json(diff)
elif self.args.markdown:
_show_markdown(diff, show_hash, hide_missing)
elif diff:
self._show_diff(diff, hide_missing)
@@ -188,6 +188,7 @@ def add_parser(subparsers, parent_parser):
nargs="?",
)
diff_parser.add_argument(
"--json",
"--show-json",
help="Format the output into a JSON",
action="store_true",
@@ -200,9 +201,11 @@ def add_parser(subparsers, parent_parser):
default=False,
)
diff_parser.add_argument(
"--md",
"--show-md",
help="Show tabulated output in the Markdown format (GFM).",
action="store_true",
dest="markdown",
default=False,
)
diff_parser.add_argument(
+101
View File
@@ -0,0 +1,101 @@
import argparse
from dvc.cli.utils import (
append_doc_link,
fix_plumbing_subparsers,
fix_subparsers,
)
from dvc.commands.experiments import (
apply,
branch,
diff,
exec_run,
gc,
init,
ls,
pull,
push,
remove,
run,
show,
)
SUB_COMMANDS = [
apply,
branch,
diff,
exec_run,
gc,
init,
ls,
pull,
push,
remove,
run,
show,
]
def add_parser(subparsers, parent_parser):
EXPERIMENTS_HELP = "Commands to run and compare experiments."
experiments_parser = subparsers.add_parser(
"experiments",
parents=[parent_parser],
aliases=["exp"],
description=append_doc_link(EXPERIMENTS_HELP, "exp"),
formatter_class=argparse.RawDescriptionHelpFormatter,
help=EXPERIMENTS_HELP,
)
experiments_subparsers = experiments_parser.add_subparsers(
dest="cmd",
help="Use `dvc experiments CMD --help` to display "
"command-specific help.",
)
fix_subparsers(experiments_subparsers)
for cmd in SUB_COMMANDS:
cmd.add_parser(experiments_subparsers, parent_parser)
fix_plumbing_subparsers(experiments_subparsers)
def add_rev_selection_flags(
experiments_subcmd_parser, command: str, default: bool = True
):
experiments_subcmd_parser.add_argument(
"-A",
"--all-commits",
action="store_true",
default=False,
help=(
f"{command} all experiments in the repository "
"(overrides `--rev` and `--num`)."
),
)
default_msg = " (HEAD by default)" if default else ""
msg = (
f"{command} experiments derived from the specified `<commit>` as "
f"baseline{default_msg}."
)
experiments_subcmd_parser.add_argument(
"--rev",
type=str,
default=None,
help=msg,
metavar="<commit>",
)
experiments_subcmd_parser.add_argument(
"-n",
"--num",
type=int,
default=1,
dest="num",
metavar="<num>",
help=(
f"{command} experiments from the last `num` commits "
"(first parents) starting from the `--rev` baseline. "
"Give a negative value to include all first-parent commits "
"(similar to `git log -n`)."
),
)
+42
View File
@@ -0,0 +1,42 @@
import argparse
import logging
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
logger = logging.getLogger(__name__)
class CmdExperimentsApply(CmdBase):
def run(self):
self.repo.experiments.apply(
self.args.experiment, force=self.args.force
)
return 0
def add_parser(experiments_subparsers, parent_parser):
EXPERIMENTS_APPLY_HELP = (
"Apply the changes from an experiment to your workspace."
)
experiments_apply_parser = experiments_subparsers.add_parser(
"apply",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_APPLY_HELP, "exp/apply"),
help=EXPERIMENTS_APPLY_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
experiments_apply_parser.add_argument(
"--no-force",
action="store_false",
dest="force",
help="Fail if this command would overwrite conflicting changes.",
)
experiments_apply_parser.add_argument(
"experiment", help="Experiment to be applied."
).complete = completion.EXPERIMENT
experiments_apply_parser.set_defaults(func=CmdExperimentsApply)
+34
View File
@@ -0,0 +1,34 @@
import argparse
import logging
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
logger = logging.getLogger(__name__)
class CmdExperimentsBranch(CmdBase):
def run(self):
self.repo.experiments.branch(self.args.experiment, self.args.branch)
return 0
def add_parser(experiments_subparsers, parent_parser):
EXPERIMENTS_BRANCH_HELP = "Promote an experiment to a Git branch."
experiments_branch_parser = experiments_subparsers.add_parser(
"branch",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_BRANCH_HELP, "exp/branch"),
help=EXPERIMENTS_BRANCH_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
experiments_branch_parser.add_argument(
"experiment", help="Experiment to be promoted."
)
experiments_branch_parser.add_argument(
"branch", help="Git branch name to use."
)
experiments_branch_parser.set_defaults(func=CmdExperimentsBranch)
+118
View File
@@ -0,0 +1,118 @@
import argparse
import logging
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.commands.metrics import DEFAULT_PRECISION
from dvc.exceptions import DvcException
from dvc.ui import ui
logger = logging.getLogger(__name__)
class CmdExperimentsDiff(CmdBase):
def run(self):
try:
diff = self.repo.experiments.diff(
a_rev=self.args.a_rev,
b_rev=self.args.b_rev,
all=self.args.all,
param_deps=self.args.param_deps,
)
except DvcException:
logger.exception("failed to show experiments diff")
return 1
if self.args.json:
ui.write_json(diff)
else:
from dvc.compare import show_diff
precision = self.args.precision or DEFAULT_PRECISION
diffs = [("metrics", "Metric"), ("params", "Param")]
for idx, (key, title) in enumerate(diffs):
if idx:
# we are printing tables even in `--quiet` mode
# so we should also be printing the "table" separator
ui.write(force=True)
show_diff(
diff[key],
title=title,
markdown=self.args.markdown,
no_path=self.args.no_path,
on_empty_diff="diff not supported",
precision=precision if key == "metrics" else None,
a_rev=self.args.a_rev,
b_rev=self.args.b_rev,
)
return 0
def add_parser(experiments_subparsers, parent_parser):
EXPERIMENTS_DIFF_HELP = (
"Show changes between experiments in the DVC repository."
)
experiments_diff_parser = experiments_subparsers.add_parser(
"diff",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_DIFF_HELP, "exp/diff"),
help=EXPERIMENTS_DIFF_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
experiments_diff_parser.add_argument(
"a_rev", nargs="?", help="Old experiment to compare (defaults to HEAD)"
).complete = completion.EXPERIMENT
experiments_diff_parser.add_argument(
"b_rev",
nargs="?",
help="New experiment to compare (defaults to the current workspace)",
).complete = completion.EXPERIMENT
experiments_diff_parser.add_argument(
"--all",
action="store_true",
default=False,
help="Show unchanged metrics/params as well.",
)
experiments_diff_parser.add_argument(
"--param-deps",
action="store_true",
default=False,
help="Show only params that are stage dependencies.",
)
experiments_diff_parser.add_argument(
"--json",
"--show-json",
action="store_true",
default=False,
help="Show output in JSON format.",
)
experiments_diff_parser.add_argument(
"--md",
"--show-md",
action="store_true",
default=False,
dest="markdown",
help="Show tabulated output in the Markdown format (GFM).",
)
experiments_diff_parser.add_argument(
"--no-path",
action="store_true",
default=False,
help="Don't show metric/param path.",
)
experiments_diff_parser.add_argument(
"--precision",
type=int,
help=(
"Round metrics/params to `n` digits precision after the decimal "
f"point. Rounds to {DEFAULT_PRECISION} digits by default."
),
metavar="<n>",
)
experiments_diff_parser.set_defaults(func=CmdExperimentsDiff)
+42
View File
@@ -0,0 +1,42 @@
import logging
from dvc.cli.command import CmdBaseNoRepo
logger = logging.getLogger(__name__)
class CmdExecutorRun(CmdBaseNoRepo):
"""Run an experiment executor."""
def run(self):
from dvc.repo.experiments.executor.base import (
BaseExecutor,
ExecutorInfo,
)
from dvc.utils.serialize import load_json
info = ExecutorInfo.from_dict(load_json(self.args.infofile))
BaseExecutor.reproduce(
info=info,
rev="",
queue=None,
log_level=logger.getEffectiveLevel(),
infofile=self.args.infofile,
)
return 0
def add_parser(experiments_subparsers, parent_parser):
EXEC_RUN_HELP = "Run an experiment executor."
exec_run_parser = experiments_subparsers.add_parser(
"exec-run",
parents=[parent_parser],
description=EXEC_RUN_HELP,
add_help=False,
)
exec_run_parser.add_argument(
"--infofile",
help="Path to executor info file",
default=None,
)
exec_run_parser.set_defaults(func=CmdExecutorRun)
+129
View File
@@ -0,0 +1,129 @@
import argparse
import logging
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import InvalidArgumentError
from dvc.ui import ui
logger = logging.getLogger(__name__)
def _raise_error_if_all_disabled(**kwargs):
if not any(kwargs.values()):
raise InvalidArgumentError(
"Either of `-w|--workspace`, `-a|--all-branches`, `-T|--all-tags` "
"or `--all-commits` needs to be set."
)
class CmdExperimentsGC(CmdBase):
def run(self):
_raise_error_if_all_disabled(
all_branches=self.args.all_branches,
all_tags=self.args.all_tags,
all_commits=self.args.all_commits,
workspace=self.args.workspace,
)
msg = "This will remove all experiments except those derived from "
msg += "the workspace"
if self.args.all_commits:
msg += " and all git commits"
elif self.args.all_branches and self.args.all_tags:
msg += " and all git branches and tags"
elif self.args.all_branches:
msg += " and all git branches"
elif self.args.all_tags:
msg += " and all git tags"
msg += " of the current repo."
if self.args.queued:
msg += " Run queued experiments will be preserved."
else:
msg += " Run queued experiments will be removed."
logger.warning(msg)
msg = "Are you sure you want to proceed?"
if not self.args.force and not ui.confirm(msg):
return 1
removed = self.repo.experiments.gc(
all_branches=self.args.all_branches,
all_tags=self.args.all_tags,
all_commits=self.args.all_commits,
workspace=self.args.workspace,
queued=self.args.queued,
)
if removed:
ui.write(
f"Removed {removed} experiments.",
"To remove unused cache files",
"use 'dvc gc'.",
)
else:
ui.write("No experiments to remove.")
return 0
def add_parser(experiments_subparsers, parent_parser):
EXPERIMENTS_GC_HELP = "Garbage collect unneeded experiments."
EXPERIMENTS_GC_DESCRIPTION = (
"Removes all experiments which are not derived from the specified"
"Git revisions."
)
experiments_gc_parser = experiments_subparsers.add_parser(
"gc",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_GC_DESCRIPTION, "exp/gc"),
help=EXPERIMENTS_GC_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
experiments_gc_parser.add_argument(
"-w",
"--workspace",
action="store_true",
default=False,
help="Keep experiments derived from the current workspace.",
)
experiments_gc_parser.add_argument(
"-a",
"--all-branches",
action="store_true",
default=False,
help="Keep experiments derived from the tips of all Git branches.",
)
experiments_gc_parser.add_argument(
"-T",
"--all-tags",
action="store_true",
default=False,
help="Keep experiments derived from all Git tags.",
)
experiments_gc_parser.add_argument(
"-A",
"--all-commits",
action="store_true",
default=False,
help="Keep experiments derived from all Git commits.",
)
experiments_gc_parser.add_argument(
"--queued",
action="store_true",
default=False,
help=(
"Keep queued experiments (experiments run queue will be cleared "
"by default)."
),
)
experiments_gc_parser.add_argument(
"-f",
"--force",
action="store_true",
default=False,
help="Force garbage collection - automatically agree to all prompts.",
)
experiments_gc_parser.set_defaults(func=CmdExperimentsGC)
+210
View File
@@ -0,0 +1,210 @@
import argparse
import logging
from typing import TYPE_CHECKING, List
from funcy import compact
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import InvalidArgumentError
from dvc.ui import ui
if TYPE_CHECKING:
from dvc.dependency import Dependency
from dvc.stage import PipelineStage
logger = logging.getLogger(__name__)
class CmdExperimentsInit(CmdBase):
DEFAULT_NAME = "train"
CODE = "src"
DATA = "data"
MODELS = "models"
DEFAULT_METRICS = "metrics.json"
DEFAULT_PARAMS = "params.yaml"
PLOTS = "plots"
DEFAULTS = {
"code": CODE,
"data": DATA,
"models": MODELS,
"metrics": DEFAULT_METRICS,
"params": DEFAULT_PARAMS,
"plots": PLOTS,
}
def run(self):
from dvc.commands.stage import parse_cmd
cmd = parse_cmd(self.args.command)
if not self.args.interactive and not cmd:
raise InvalidArgumentError("command is not specified")
from dvc.repo.experiments.init import init
defaults = {}
if not self.args.explicit:
config = self.repo.config["exp"]
defaults.update({**self.DEFAULTS, **config})
cli_args = compact(
{
"cmd": cmd,
"code": self.args.code,
"data": self.args.data,
"models": self.args.models,
"metrics": self.args.metrics,
"params": self.args.params,
"plots": self.args.plots,
"live": self.args.live,
}
)
initialized_stage, initialized_deps, initialized_out_dirs = init(
self.repo,
name=self.args.name,
type=self.args.type,
defaults=defaults,
overrides=cli_args,
interactive=self.args.interactive,
force=self.args.force,
)
self._post_init_display(
initialized_stage, initialized_deps, initialized_out_dirs
)
if self.args.run:
self.repo.experiments.run(targets=[initialized_stage.addressing])
return 0
def _post_init_display(
self,
stage: "PipelineStage",
new_deps: List["Dependency"],
new_out_dirs: List[str],
) -> None:
from dvc.utils import humanize
path_fmt = "[green]{}[/green]".format
if new_deps:
deps_paths = humanize.join(map(path_fmt, new_deps))
ui.write(f"Creating dependencies: {deps_paths}", styled=True)
if new_out_dirs:
out_dirs_paths = humanize.join(map(path_fmt, new_out_dirs))
ui.write(
f"Creating output directories: {out_dirs_paths}", styled=True
)
ui.write(
f"Creating [b]{self.args.name}[/b] stage in [green]dvc.yaml[/]",
styled=True,
)
if stage.outs or not self.args.run:
# separate the above status-like messages with help/tips section
ui.write(styled=True)
if stage.outs:
outs_paths = humanize.join(map(path_fmt, stage.outs))
tips = f"Ensure your experiment command creates {outs_paths}."
ui.write(tips, styled=True)
if not self.args.run:
ui.write(
'You can now run your experiment using [b]"dvc exp run"[/].',
styled=True,
)
else:
# separate between `exp.run` output and `dvc exp init` output
ui.write(styled=True)
def add_parser(experiments_subparsers, parent_parser):
EXPERIMENTS_INIT_HELP = "Quickly setup any project to use experiments."
experiments_init_parser = experiments_subparsers.add_parser(
"init",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_INIT_HELP, "exp/init"),
formatter_class=argparse.RawDescriptionHelpFormatter,
help=EXPERIMENTS_INIT_HELP,
)
experiments_init_parser.add_argument(
"command",
nargs=argparse.REMAINDER,
help="Command to execute.",
metavar="command",
)
experiments_init_parser.add_argument(
"--run",
action="store_true",
help="Run the experiment after initializing it",
)
experiments_init_parser.add_argument(
"--interactive",
"-i",
action="store_true",
help="Prompt for values that are not provided",
)
experiments_init_parser.add_argument(
"-f",
"--force",
action="store_true",
default=False,
help="Overwrite existing stage",
)
experiments_init_parser.add_argument(
"--explicit",
action="store_true",
default=False,
help="Only use the path values explicitly provided",
)
experiments_init_parser.add_argument(
"--name",
"-n",
help="Name of the stage to create (default: %(default)s)",
default=CmdExperimentsInit.DEFAULT_NAME,
)
experiments_init_parser.add_argument(
"--code",
help="Path to the source file or directory "
"which your experiments depend"
f" (default: {CmdExperimentsInit.CODE})",
)
experiments_init_parser.add_argument(
"--data",
help="Path to the data file or directory "
"which your experiments depend"
f" (default: {CmdExperimentsInit.DATA})",
)
experiments_init_parser.add_argument(
"--models",
help="Path to the model file or directory for your experiments"
f" (default: {CmdExperimentsInit.MODELS})",
)
experiments_init_parser.add_argument(
"--params",
help="Path to the parameters file for your experiments"
f" (default: {CmdExperimentsInit.DEFAULT_PARAMS})",
)
experiments_init_parser.add_argument(
"--metrics",
help="Path to the metrics file for your experiments"
f" (default: {CmdExperimentsInit.DEFAULT_METRICS})",
)
experiments_init_parser.add_argument(
"--plots",
help="Path to the plots file or directory for your experiments"
f" (default: {CmdExperimentsInit.PLOTS})",
)
experiments_init_parser.add_argument(
"--live",
help="Path to log dvclive outputs for your experiments",
)
experiments_init_parser.add_argument(
"--type",
choices=["default", "checkpoint"],
default="default",
help="Select type of stage to create (default: %(default)s)",
)
experiments_init_parser.set_defaults(func=CmdExperimentsInit)
+64
View File
@@ -0,0 +1,64 @@
import argparse
import logging
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
logger = logging.getLogger(__name__)
class CmdExperimentsList(CmdBase):
def run(self):
name_only = self.args.name_only
exps = self.repo.experiments.ls(
all_commits=self.args.all_commits,
rev=self.args.rev,
num=self.args.num,
git_remote=self.args.git_remote,
)
for baseline in exps:
tag = self.repo.scm.describe(baseline)
if not tag:
branch = self.repo.scm.describe(baseline, base="refs/heads")
if branch:
tag = branch.split("/")[-1]
name = tag if tag else baseline[:7]
if not name_only:
print(f"{name}:")
for exp_name in exps[baseline]:
indent = "" if name_only else "\t"
print(f"{indent}{exp_name}")
return 0
def add_parser(experiments_subparsers, parent_parser):
from . import add_rev_selection_flags
EXPERIMENTS_LIST_HELP = "List local and remote experiments."
experiments_list_parser = experiments_subparsers.add_parser(
"list",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_LIST_HELP, "exp/list"),
help=EXPERIMENTS_LIST_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
add_rev_selection_flags(experiments_list_parser, "List")
experiments_list_parser.add_argument(
"--name-only",
"--names-only",
action="store_true",
help="Only output experiment names (without parent commits).",
)
experiments_list_parser.add_argument(
"git_remote",
nargs="?",
default=None,
help=(
"Optional Git remote name or Git URL. "
"If provided, experiments from the specified Git repository "
" will be listed instead of local ones."
),
metavar="<git_remote>",
)
experiments_list_parser.set_defaults(func=CmdExperimentsList)
+117
View File
@@ -0,0 +1,117 @@
import argparse
import logging
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import InvalidArgumentError
from dvc.ui import ui
logger = logging.getLogger(__name__)
class CmdExperimentsPull(CmdBase):
def raise_error_if_all_disabled(self):
if not any(
[self.args.experiment, self.args.all_commits, self.args.rev]
):
raise InvalidArgumentError(
"Either provide an `experiment` argument, or use the "
"`--rev` or `--all-commits` flag."
)
def run(self):
self.raise_error_if_all_disabled()
pulled_exps = self.repo.experiments.pull(
self.args.git_remote,
self.args.experiment,
all_commits=self.args.all_commits,
rev=self.args.rev,
num=self.args.num,
force=self.args.force,
pull_cache=self.args.pull_cache,
dvc_remote=self.args.dvc_remote,
jobs=self.args.jobs,
run_cache=self.args.run_cache,
)
if pulled_exps:
ui.write(
f"Pulled experiment '{pulled_exps}'",
f"from Git remote '{self.args.git_remote}'.",
)
else:
ui.write("No experiments to pull.")
if not self.args.pull_cache:
ui.write(
"To pull cached outputs for this experiment"
"from DVC remote storage,"
"re-run this command without '--no-cache'."
)
return 0
def add_parser(experiments_subparsers, parent_parser):
from . import add_rev_selection_flags
EXPERIMENTS_PULL_HELP = "Pull an experiment from a Git remote."
experiments_pull_parser = experiments_subparsers.add_parser(
"pull",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_PULL_HELP, "exp/pull"),
help=EXPERIMENTS_PULL_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
add_rev_selection_flags(experiments_pull_parser, "Pull", False)
experiments_pull_parser.add_argument(
"-f",
"--force",
action="store_true",
help="Replace local experiment already exists.",
)
experiments_pull_parser.add_argument(
"--no-cache",
action="store_false",
dest="pull_cache",
help=(
"Do not pull cached outputs for this experiment from DVC remote "
"storage."
),
)
experiments_pull_parser.add_argument(
"-r",
"--remote",
dest="dvc_remote",
metavar="<name>",
help="Name of the DVC remote to use when pulling cached outputs.",
)
experiments_pull_parser.add_argument(
"-j",
"--jobs",
type=int,
metavar="<number>",
help=(
"Number of jobs to run simultaneously when pulling from DVC "
"remote storage."
),
)
experiments_pull_parser.add_argument(
"--run-cache",
action="store_true",
default=False,
help="Pull run history for all stages.",
)
experiments_pull_parser.add_argument(
"git_remote",
help="Git remote name or Git URL.",
metavar="<git_remote>",
)
experiments_pull_parser.add_argument(
"experiment",
nargs="*",
default=None,
help="Experiments to pull.",
metavar="<experiment>",
)
experiments_pull_parser.set_defaults(func=CmdExperimentsPull)
+119
View File
@@ -0,0 +1,119 @@
import argparse
import logging
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import InvalidArgumentError
from dvc.ui import ui
logger = logging.getLogger(__name__)
class CmdExperimentsPush(CmdBase):
def raise_error_if_all_disabled(self):
if not any(
[self.args.experiment, self.args.all_commits, self.args.rev]
):
raise InvalidArgumentError(
"Either provide an `experiment` argument, or use the "
"`--rev` or `--all-commits` flag."
)
def run(self):
self.raise_error_if_all_disabled()
pushed_exps = self.repo.experiments.push(
self.args.git_remote,
self.args.experiment,
all_commits=self.args.all_commits,
rev=self.args.rev,
num=self.args.num,
force=self.args.force,
push_cache=self.args.push_cache,
dvc_remote=self.args.dvc_remote,
jobs=self.args.jobs,
run_cache=self.args.run_cache,
)
if pushed_exps:
ui.write(
f"Pushed experiment '{pushed_exps}'"
f"to Git remote '{self.args.git_remote}'."
)
else:
ui.write("No experiments to push.")
if not self.args.push_cache:
ui.write(
"To push cached outputs",
"for this experiment to DVC remote storage,"
"re-run this command without '--no-cache'.",
)
return 0
def add_parser(experiments_subparsers, parent_parser):
from . import add_rev_selection_flags
EXPERIMENTS_PUSH_HELP = "Push a local experiment to a Git remote."
experiments_push_parser = experiments_subparsers.add_parser(
"push",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_PUSH_HELP, "exp/push"),
help=EXPERIMENTS_PUSH_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
add_rev_selection_flags(experiments_push_parser, "Push", False)
experiments_push_parser.add_argument(
"-f",
"--force",
action="store_true",
help="Replace experiment in the Git remote if it already exists.",
)
experiments_push_parser.add_argument(
"--no-cache",
action="store_false",
dest="push_cache",
help=(
"Do not push cached outputs for this experiment to DVC remote "
"storage."
),
)
experiments_push_parser.add_argument(
"-r",
"--remote",
dest="dvc_remote",
metavar="<name>",
help="Name of the DVC remote to use when pushing cached outputs.",
)
experiments_push_parser.add_argument(
"-j",
"--jobs",
type=int,
metavar="<number>",
help=(
"Number of jobs to run simultaneously when pushing to DVC remote "
"storage."
),
)
experiments_push_parser.add_argument(
"--run-cache",
action="store_true",
default=False,
help="Push run history for all stages.",
)
experiments_push_parser.add_argument(
"git_remote",
help="Git remote name or Git URL.",
metavar="<git_remote>",
)
experiments_push_parser.add_argument(
"experiment",
nargs="*",
default=None,
help="Experiments to push.",
metavar="<experiment>",
).complete = completion.EXPERIMENT
experiments_push_parser.set_defaults(func=CmdExperimentsPush)
+73
View File
@@ -0,0 +1,73 @@
import argparse
import logging
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import InvalidArgumentError
from dvc.ui import ui
logger = logging.getLogger(__name__)
class CmdExperimentsRemove(CmdBase):
def raise_error_if_all_disabled(self):
if not any(
[
self.args.experiment,
self.args.all_commits,
self.args.rev,
self.args.queue,
]
):
raise InvalidArgumentError(
"Either provide an `experiment` argument, or use the "
"`--rev` or `--all-commits` flag."
)
def run(self):
self.raise_error_if_all_disabled()
removed_list = self.repo.experiments.remove(
exp_names=self.args.experiment,
all_commits=self.args.all_commits,
rev=self.args.rev,
num=self.args.num,
queue=self.args.queue,
git_remote=self.args.git_remote,
)
removed = ",".join(removed_list)
ui.write(f"Removed experiments: {removed}")
return 0
def add_parser(experiments_subparsers, parent_parser):
from . import add_rev_selection_flags
EXPERIMENTS_REMOVE_HELP = "Remove experiments."
experiments_remove_parser = experiments_subparsers.add_parser(
"remove",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_REMOVE_HELP, "exp/remove"),
help=EXPERIMENTS_REMOVE_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
remove_group = experiments_remove_parser.add_mutually_exclusive_group()
add_rev_selection_flags(experiments_remove_parser, "Remove", False)
remove_group.add_argument(
"--queue", action="store_true", help="Remove all queued experiments."
)
remove_group.add_argument(
"-g",
"--git-remote",
metavar="<git_remote>",
help="Name or URL of the Git remote to remove the experiment from",
)
experiments_remove_parser.add_argument(
"experiment",
nargs="*",
help="Experiments to remove.",
metavar="<experiment>",
)
experiments_remove_parser.set_defaults(func=CmdExperimentsRemove)
+142
View File
@@ -0,0 +1,142 @@
import argparse
import logging
from dvc.cli import completion
from dvc.cli.utils import append_doc_link
from dvc.commands.repro import CmdRepro
from dvc.commands.repro import add_arguments as add_repro_arguments
from dvc.exceptions import InvalidArgumentError
from dvc.ui import ui
logger = logging.getLogger(__name__)
class CmdExperimentsRun(CmdRepro):
def run(self):
from dvc.compare import show_metrics
if self.args.checkpoint_resume:
if self.args.reset:
raise InvalidArgumentError(
"--reset and --rev are mutually exclusive."
)
if not (self.args.queue or self.args.tmp_dir):
raise InvalidArgumentError(
"--rev can only be used in conjunction with "
"--queue or --temp."
)
if self.args.reset:
ui.write("Any existing checkpoints will be reset and re-run.")
results = self.repo.experiments.run(
name=self.args.name,
queue=self.args.queue,
run_all=self.args.run_all,
jobs=self.args.jobs,
params=self.args.set_param,
checkpoint_resume=self.args.checkpoint_resume,
reset=self.args.reset,
tmp_dir=self.args.tmp_dir,
machine=self.args.machine,
**self._common_kwargs,
)
if self.args.metrics and results:
metrics = self.repo.metrics.show(revs=list(results))
metrics.pop("workspace", None)
show_metrics(metrics)
return 0
def add_parser(experiments_subparsers, parent_parser):
EXPERIMENTS_RUN_HELP = "Run or resume an experiment."
experiments_run_parser = experiments_subparsers.add_parser(
"run",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_RUN_HELP, "exp/run"),
help=EXPERIMENTS_RUN_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
_add_run_common(experiments_run_parser)
experiments_run_parser.add_argument(
"-r",
"--rev",
type=str,
dest="checkpoint_resume",
help=(
"Continue the specified checkpoint experiment. Can only be used "
"in conjunction with --queue or --temp."
),
metavar="<experiment_rev>",
).complete = completion.EXPERIMENT
experiments_run_parser.add_argument(
"--reset",
action="store_true",
help="Reset existing checkpoints and restart the experiment.",
)
experiments_run_parser.set_defaults(func=CmdExperimentsRun)
def _add_run_common(parser):
"""Add common args for 'exp run'."""
# inherit arguments from `dvc repro`
add_repro_arguments(parser)
parser.add_argument(
"-n",
"--name",
default=None,
help=(
"Human-readable experiment name. If not specified, a name will "
"be auto-generated."
),
metavar="<name>",
)
parser.add_argument(
"-S",
"--set-param",
action="append",
default=[],
help="Use the specified param value when reproducing pipelines.",
metavar="[<filename>:]<param_name>=<param_value>",
)
parser.add_argument(
"--queue",
action="store_true",
default=False,
help="Stage this experiment in the run queue for future execution.",
)
parser.add_argument(
"--run-all",
action="store_true",
default=False,
help="Execute all experiments in the run queue. Implies --temp.",
)
parser.add_argument(
"-j",
"--jobs",
type=int,
default=1,
help="Run the specified number of experiments at a time in parallel.",
metavar="<number>",
)
parser.add_argument(
"--temp",
action="store_true",
dest="tmp_dir",
help=(
"Run this experiment in a separate temporary directory instead of "
"your workspace."
),
)
parser.add_argument(
"--machine",
default=None,
help=argparse.SUPPRESS,
# help=(
# "Run this experiment on the specified 'dvc machine' instance."
# )
# metavar="<name>",
)
+655
View File
@@ -0,0 +1,655 @@
import argparse
import logging
import os
import re
from collections import Counter, OrderedDict, defaultdict
from datetime import date, datetime
from typing import TYPE_CHECKING
from funcy import lmap
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.commands.metrics import DEFAULT_PRECISION
from dvc.exceptions import DvcException, InvalidArgumentError
from dvc.ui import ui
from dvc.utils.flatten import flatten
from dvc.utils.serialize import encode_exception
if TYPE_CHECKING:
from dvc.compare import TabularData
from dvc.ui import RichText
FILL_VALUE = "-"
FILL_VALUE_ERRORED = "!"
logger = logging.getLogger(__name__)
def _update_names(names, items):
for name, item in items:
item = item.get("data", {})
if isinstance(item, dict):
item = flatten(item)
names[name].update({key: None for key in item})
def _collect_names(all_experiments, **kwargs):
metric_names = defaultdict(dict)
param_names = defaultdict(dict)
deps_names = set()
for _, experiments in all_experiments.items():
for exp_data in experiments.values():
exp = exp_data.get("data", {})
_update_names(metric_names, exp.get("metrics", {}).items())
_update_names(param_names, exp.get("params", {}).items())
for dep_name in exp.get("deps", {}):
deps_names.add(dep_name)
return metric_names, param_names, sorted(deps_names)
experiment_types = {
"checkpoint_tip": "│ ╓",
"checkpoint_commit": "│ ╟",
"checkpoint_base": "├─╨",
"branch_commit": "├──",
"branch_base": "└──",
"baseline": "",
}
def _collect_rows(
base_rev,
experiments,
all_headers,
metric_headers,
param_headers,
metric_names,
param_names,
deps_names,
precision=DEFAULT_PRECISION,
sort_by=None,
sort_order=None,
fill_value=FILL_VALUE,
iso=False,
):
from scmrepo.git import Git
if sort_by:
sort_path, sort_name, sort_type = _sort_column(
sort_by, metric_names, param_names
)
reverse = sort_order == "desc"
experiments = _sort_exp(
experiments, sort_path, sort_name, sort_type, reverse
)
new_checkpoint = True
for i, (rev, results) in enumerate(experiments.items()):
fill_value = FILL_VALUE_ERRORED if results.get("error") else fill_value
row_dict = {k: fill_value for k in all_headers}
exp = results.get("data", {})
if exp.get("running"):
state = "Running"
elif exp.get("queued"):
state = "Queued"
else:
state = fill_value
is_baseline = rev == "baseline"
if is_baseline:
name_rev = base_rev[:7] if Git.is_sha(base_rev) else base_rev
else:
name_rev = rev[:7]
tip = exp.get("checkpoint_tip")
parent_rev = exp.get("checkpoint_parent", "")
parent_exp = experiments.get(parent_rev, {}).get("data", {})
parent_tip = parent_exp.get("checkpoint_tip")
parent = ""
if is_baseline:
typ = "baseline"
elif tip:
if tip == parent_tip:
typ = (
"checkpoint_tip" if new_checkpoint else "checkpoint_commit"
)
elif parent_rev == base_rev:
typ = "checkpoint_base"
else:
typ = "checkpoint_commit"
parent = parent_rev[:7]
elif i < len(experiments) - 1:
typ = "branch_commit"
else:
typ = "branch_base"
if not is_baseline:
new_checkpoint = not (tip and tip == parent_tip)
row_dict["Experiment"] = exp.get("name", "")
row_dict["rev"] = name_rev
row_dict["typ"] = typ
row_dict["Created"] = _format_time(
exp.get("timestamp"), fill_value, iso
)
row_dict["parent"] = parent
row_dict["State"] = state
row_dict["Executor"] = exp.get("executor", fill_value)
_extend_row(
row_dict,
metric_names,
metric_headers,
exp.get("metrics", {}).items(),
precision,
fill_value=fill_value,
)
_extend_row(
row_dict,
param_names,
param_headers,
exp.get("params", {}).items(),
precision,
fill_value=fill_value,
)
for dep in deps_names:
hash_info = exp.get("deps", {}).get(dep, {}).get("hash")
if hash_info is not None:
hash_info = hash_info[:7]
row_dict[dep] = hash_info
yield list(row_dict.values())
def _sort_column(sort_by, metric_names, param_names):
path, _, sort_name = sort_by.rpartition(":")
matches = set()
if path:
if path in metric_names and sort_name in metric_names[path]:
matches.add((path, sort_name, "metrics"))
if path in param_names and sort_name in param_names[path]:
matches.add((path, sort_name, "params"))
else:
for path in metric_names:
if sort_name in metric_names[path]:
matches.add((path, sort_name, "metrics"))
for path in param_names:
if sort_name in param_names[path]:
matches.add((path, sort_name, "params"))
if len(matches) == 1:
return matches.pop()
if len(matches) > 1:
raise InvalidArgumentError(
"Ambiguous sort column '{}' matched '{}'".format(
sort_by,
", ".join([f"{path}:{name}" for path, name, _ in matches]),
)
)
raise InvalidArgumentError(f"Unknown sort column '{sort_by}'")
def _sort_exp(experiments, sort_path, sort_name, typ, reverse):
def _sort(item):
rev, exp = item
exp_data = exp.get("data", {})
tip = exp_data.get("checkpoint_tip")
if tip and tip != rev:
# Sort checkpoint experiments by tip commit
return _sort((tip, experiments[tip]))
data = exp_data.get(typ, {}).get(sort_path, {}).get("data", {})
val = flatten(data).get(sort_name)
return val is None, val
ret = OrderedDict()
if "baseline" in experiments:
ret["baseline"] = experiments.pop("baseline")
ret.update(sorted(experiments.items(), key=_sort, reverse=reverse))
return ret
def _format_time(datetime_obj, fill_value=FILL_VALUE, iso=False):
if datetime_obj is None:
return fill_value
if iso:
return datetime_obj.isoformat()
if datetime_obj.date() == date.today():
fmt = "%I:%M %p"
else:
fmt = "%b %d, %Y"
return datetime_obj.strftime(fmt)
def _extend_row(row, names, headers, items, precision, fill_value=FILL_VALUE):
from dvc.compare import _format_field, with_value
for fname, data in items:
item = data.get("data", {})
item = flatten(item) if isinstance(item, dict) else {fname: item}
for name in names[fname]:
value = with_value(
item.get(name),
FILL_VALUE_ERRORED if data.get("error", None) else fill_value,
)
# wrap field data in ui.rich_text, otherwise rich may
# interpret unescaped braces from list/dict types as rich
# markup tags
value = ui.rich_text(str(_format_field(value, precision)))
if name in headers:
row[name] = value
else:
row[f"{fname}:{name}"] = value
def experiments_table(
all_experiments,
headers,
metric_headers,
metric_names,
param_headers,
param_names,
deps_names,
sort_by=None,
sort_order=None,
precision=DEFAULT_PRECISION,
fill_value=FILL_VALUE,
iso=False,
) -> "TabularData":
from funcy import lconcat
from dvc.compare import TabularData
all_headers = lconcat(headers, metric_headers, param_headers, deps_names)
td = TabularData(all_headers, fill_value=fill_value)
for base_rev, experiments in all_experiments.items():
rows = _collect_rows(
base_rev,
experiments,
all_headers,
metric_headers,
param_headers,
metric_names,
param_names,
deps_names,
sort_by=sort_by,
sort_order=sort_order,
precision=precision,
fill_value=fill_value,
iso=iso,
)
td.extend(rows)
return td
def prepare_exp_id(kwargs) -> "RichText":
exp_name = kwargs["Experiment"]
rev = kwargs["rev"]
typ = kwargs.get("typ", "baseline")
if typ == "baseline" or not exp_name:
text = ui.rich_text(exp_name or rev)
else:
text = ui.rich_text.assemble(rev, " [", (exp_name, "bold"), "]")
parent = kwargs.get("parent")
suff = f" ({parent})" if parent else ""
text.append(suff)
tree = experiment_types[typ]
pref = f"{tree} " if tree else ""
return ui.rich_text(pref) + text
def baseline_styler(typ):
return {"style": "bold"} if typ == "baseline" else {}
def show_experiments(
all_experiments,
keep=None,
drop=None,
pager=True,
csv=False,
markdown=False,
pcp=False,
**kwargs,
):
from funcy.seqs import flatten as flatten_list
metric_names, param_names, deps_names = _collect_names(all_experiments)
headers = [
"Experiment",
"rev",
"typ",
"Created",
"parent",
"State",
"Executor",
]
names = {**metric_names, **param_names}
counter = Counter(flatten_list([list(a.keys()) for a in names.values()]))
counter.update(headers)
metric_headers = _normalize_headers(metric_names, counter)
param_headers = _normalize_headers(param_names, counter)
td = experiments_table(
all_experiments,
headers,
metric_headers,
metric_names,
param_headers,
param_names,
deps_names,
kwargs.get("sort_by"),
kwargs.get("sort_order"),
kwargs.get("precision"),
kwargs.get("fill_value"),
kwargs.get("iso"),
)
if keep:
for col in td.keys():
if re.match(keep, col):
td.protect(col)
for col in ("State", "Executor"):
if td.is_empty(col):
td.drop(col)
row_styles = lmap(baseline_styler, td.column("typ"))
if not csv:
merge_headers = ["Experiment", "rev", "typ", "parent"]
td.column("Experiment")[:] = map(
prepare_exp_id, td.as_dict(merge_headers)
)
td.drop(*merge_headers[1:])
headers = {
"metrics": metric_headers,
"params": param_headers,
"deps": deps_names,
}
styles = {
"Experiment": {"no_wrap": True, "header_style": "black on grey93"},
"Created": {"header_style": "black on grey93"},
"State": {"header_style": "black on grey93"},
"Executor": {"header_style": "black on grey93"},
}
header_bg_colors = {
"metrics": "cornsilk1",
"params": "light_cyan1",
"deps": "plum2",
}
styles.update(
{
header: {
"justify": "right" if typ == "metrics" else "left",
"header_style": f"black on {header_bg_colors[typ]}",
"collapse": idx != 0,
"no_wrap": typ == "metrics",
}
for typ, hs in headers.items()
for idx, header in enumerate(hs)
}
)
if kwargs.get("only_changed", False) or pcp:
td.drop_duplicates("cols", ignore_empty=False)
cols_to_drop = set()
if drop is not None:
cols_to_drop = {col for col in td.keys() if re.match(drop, col)}
if pcp:
cols_to_drop.add("Created")
td.drop(*cols_to_drop)
if pcp:
subset = {x for x in td.keys() if x != "Experiment"}
td.dropna(
"rows",
how="all",
subset=subset,
)
td.drop_duplicates("rows", subset=subset)
if "Experiment" in td:
td.column("Experiment")[:] = [
# remove tree characters
str(x).encode("ascii", "ignore").strip().decode()
for x in td.column("Experiment")
]
out = kwargs.get("out") or "dvc_plots"
output_file = os.path.join(out, "index.html")
ui.write(
td.to_parallel_coordinates(
output_path=os.path.abspath(output_file),
color_by=kwargs.get("sort_by") or "Experiment",
)
)
if kwargs.get("open"):
return ui.open_browser(output_file)
else:
td.render(
pager=pager,
borders="horizontals",
rich_table=True,
header_styles=styles,
row_styles=row_styles,
csv=csv,
markdown=markdown,
)
def _normalize_headers(names, count):
return [
name if count[name] == 1 else f"{path}:{name}"
for path in names
for name in names[path]
]
def _format_json(item):
if isinstance(item, (date, datetime)):
return item.isoformat()
return encode_exception(item)
class CmdExperimentsShow(CmdBase):
def run(self):
try:
all_experiments = self.repo.experiments.show(
all_branches=self.args.all_branches,
all_tags=self.args.all_tags,
all_commits=self.args.all_commits,
revs=self.args.rev,
num=self.args.num,
sha_only=self.args.sha,
param_deps=self.args.param_deps,
fetch_running=self.args.fetch_running,
)
except DvcException:
logger.exception("failed to show experiments")
return 1
if self.args.json:
ui.write_json(all_experiments, default=_format_json)
else:
precision = (
self.args.precision or None
if self.args.csv
else DEFAULT_PRECISION
)
fill_value = "" if self.args.csv else FILL_VALUE
iso = True if self.args.csv else False
show_experiments(
all_experiments,
keep=self.args.keep,
drop=self.args.drop,
sort_by=self.args.sort_by,
sort_order=self.args.sort_order,
precision=precision,
fill_value=fill_value,
iso=iso,
pager=not self.args.no_pager,
csv=self.args.csv,
markdown=self.args.markdown,
only_changed=self.args.only_changed,
pcp=self.args.pcp,
out=self.args.out,
open=self.args.open,
)
return 0
def add_parser(experiments_subparsers, parent_parser):
from . import add_rev_selection_flags
EXPERIMENTS_SHOW_HELP = "Print experiments."
experiments_show_parser = experiments_subparsers.add_parser(
"show",
parents=[parent_parser],
description=append_doc_link(EXPERIMENTS_SHOW_HELP, "exp/show"),
help=EXPERIMENTS_SHOW_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
add_rev_selection_flags(experiments_show_parser, "Show")
experiments_show_parser.add_argument(
"-a",
"--all-branches",
action="store_true",
default=False,
help="Show experiments derived from the tip of all Git branches.",
)
experiments_show_parser.add_argument(
"-T",
"--all-tags",
action="store_true",
default=False,
help="Show experiments derived from all Git tags.",
)
experiments_show_parser.add_argument(
"--no-pager",
action="store_true",
default=False,
help="Do not pipe output into a pager.",
)
experiments_show_parser.add_argument(
"--only-changed",
action="store_true",
default=False,
help=(
"Only show metrics/params with values varying "
"across the selected experiments."
),
)
experiments_show_parser.add_argument(
"--drop",
help="Remove the columns matching the specified regex pattern.",
metavar="<regex_pattern>",
)
experiments_show_parser.add_argument(
"--keep",
help="Preserve the columns matching the specified regex pattern.",
metavar="<regex_pattern>",
)
experiments_show_parser.add_argument(
"--param-deps",
action="store_true",
default=False,
help="Show only params that are stage dependencies.",
)
experiments_show_parser.add_argument(
"--sort-by",
help="Sort related experiments by the specified metric or param.",
metavar="<metric/param>",
)
experiments_show_parser.add_argument(
"--sort-order",
help=(
"Sort order to use with --sort-by."
" Defaults to ascending ('asc')."
),
choices=("asc", "desc"),
default="asc",
)
experiments_show_parser.add_argument(
"--sha",
action="store_true",
default=False,
help="Always show git commit SHAs instead of branch/tag names.",
)
experiments_show_parser.add_argument(
"--json",
"--show-json",
action="store_true",
default=False,
help="Print output in JSON format instead of a human-readable table.",
)
experiments_show_parser.add_argument(
"--csv",
"--show-csv",
action="store_true",
default=False,
help="Print output in csv format instead of a human-readable table.",
)
experiments_show_parser.add_argument(
"--md",
"--show-md",
action="store_true",
default=False,
dest="markdown",
help="Show tabulated output in the Markdown format (GFM).",
)
experiments_show_parser.add_argument(
"--precision",
type=int,
help=(
"Round metrics/params to `n` digits precision after the decimal "
f"point. Rounds to {DEFAULT_PRECISION} digits by default."
),
metavar="<n>",
)
experiments_show_parser.add_argument(
"--parallel-coordinates-plot",
"--pcp",
dest="pcp",
action="store_true",
default=False,
help="Generate a Parallel Coordinates Plot from the tabulated output.",
)
experiments_show_parser.add_argument(
"-o",
"--out",
default=None,
help="Destination folder to save the Parallel Coordinates Plot to",
metavar="<path>",
).complete = completion.DIR
experiments_show_parser.add_argument(
"--open",
action="store_true",
default=False,
help="Open the Parallel Coordinates Plot directly in the browser.",
)
experiments_show_parser.add_argument(
"--no-fetch",
dest="fetch_running",
action="store_false",
help=argparse.SUPPRESS,
)
experiments_show_parser.set_defaults(func=CmdExperimentsShow)
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
+2 -2
View File
@@ -2,7 +2,8 @@ import argparse
import logging
import os
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
logger = logging.getLogger(__name__)
@@ -136,7 +137,6 @@ def add_parser(subparsers, parent_parser):
help=(
"Number of jobs to run simultaneously. "
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
+3 -3
View File
@@ -1,10 +1,11 @@
import argparse
import logging
from dvc.cli import completion
from dvc.cli.command import CmdBaseNoRepo
from dvc.exceptions import DvcException
from . import completion
from .base import CmdBaseNoRepo, append_doc_link
from ..cli.utils import append_doc_link
logger = logging.getLogger(__name__)
@@ -93,7 +94,6 @@ def add_parser(subparsers, parent_parser):
help=(
"Number of jobs to run simultaneously. "
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
@@ -1,10 +1,11 @@
import argparse
import logging
from dvc.cli import completion
from dvc.cli.command import CmdBaseNoRepo
from dvc.exceptions import DvcException
from . import completion
from .base import CmdBaseNoRepo, append_doc_link
from ..cli.utils import append_doc_link
logger = logging.getLogger(__name__)
@@ -43,7 +44,6 @@ def add_parser(subparsers, parent_parser):
help=(
"Number of jobs to run simultaneously. "
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
@@ -1,7 +1,8 @@
import logging
import os
from dvc.command.base import CmdBaseNoRepo, fix_subparsers
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import fix_subparsers
from dvc.exceptions import NotDvcRepoError
logger = logging.getLogger(__name__)
@@ -25,7 +26,7 @@ class CmdHookBase(CmdBaseNoRepo):
class CmdPreCommit(CmdHookBase):
def _run(self):
from dvc.main import main
from dvc.cli import main
return main(["status"])
@@ -49,14 +50,14 @@ class CmdPostCheckout(CmdHookBase):
if os.path.isdir(os.path.join(".git", "rebase-merge")):
return 0
from dvc.main import main
from dvc.cli import main
return main(["checkout"])
class CmdPrePush(CmdHookBase):
def _run(self):
from dvc.main import main
from dvc.cli import main
return main(["push"])
+3 -3
View File
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
@@ -90,7 +91,6 @@ def add_parser(subparsers, parent_parser):
help=(
"Number of jobs to run simultaneously. "
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
@@ -90,7 +91,6 @@ def add_parser(subparsers, parent_parser):
help=(
"Number of jobs to run simultaneously. "
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
+2 -1
View File
@@ -4,7 +4,8 @@ import logging
import colorama
from dvc import analytics
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import append_doc_link
from dvc.utils import boxify
from dvc.utils import format_link as fmt_link
@@ -1,7 +1,8 @@
import argparse
import logging
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
@@ -1,9 +1,10 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.command.ls.ls_colors import LsColors
from dvc.cli import completion
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import append_doc_link
from dvc.commands.ls.ls_colors import LsColors
from dvc.exceptions import DvcException
from dvc.ui import ui
@@ -34,10 +35,8 @@ class CmdList(CmdBaseNoRepo):
recursive=self.args.recursive,
dvc_only=self.args.dvc_only,
)
if self.args.show_json:
import json
ui.write(json.dumps(entries))
if self.args.json:
ui.write_json(entries)
elif entries:
entries = _prettify(entries, with_color=True)
ui.write("\n".join(entries))
@@ -54,6 +53,7 @@ def add_parser(subparsers, parent_parser):
)
list_parser = subparsers.add_parser(
"list",
aliases=["ls"],
parents=[parent_parser],
description=append_doc_link(LIST_HELP, "list"),
help=LIST_HELP,
@@ -70,7 +70,10 @@ def add_parser(subparsers, parent_parser):
"--dvc-only", action="store_true", help="Show only DVC outputs."
)
list_parser.add_argument(
"--show-json", action="store_true", help="Show output in JSON format."
"--json",
"--show-json",
action="store_true",
help="Show output in JSON format.",
)
list_parser.add_argument(
"--rev",
@@ -1,8 +1,12 @@
import argparse
from dvc.command.base import CmdBase, append_doc_link, fix_subparsers
from dvc.command.config import CmdConfig
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link, fix_subparsers
from dvc.commands.config import CmdConfig
from dvc.compare import TabularData
from dvc.config import ConfigError
from dvc.exceptions import DvcException
from dvc.types import Dict, List
from dvc.ui import ui
from dvc.utils import format_link
@@ -70,18 +74,56 @@ class CmdMachineRemove(CmdMachineConfig):
class CmdMachineList(CmdMachineConfig):
def run(self):
TABLE_COLUMNS = [
"name",
"cloud",
"region",
"image",
"spot",
"spot_price",
"instance_hdd_size",
"instance_type",
"ssh_private",
"startup_script",
]
PRIVATE_COLUMNS = ["ssh_private", "startup_script"]
def _hide_private(self, conf):
for machine in conf:
for column in self.PRIVATE_COLUMNS:
if column in conf[machine]:
conf[machine][column] = "***"
def _show_origin(self):
levels = [self.args.level] if self.args.level else self.config.LEVELS
for level in levels:
conf = self.config.read(level)["machine"]
if self.args.name:
conf = conf.get(self.args.name, {})
prefix = self._config_file_prefix(
self.args.show_origin, self.config, level
)
self._hide_private(conf)
prefix = self._config_file_prefix(True, self.config, level)
configs = list(self._format_config(conf, prefix))
if configs:
ui.write("\n".join(configs))
def _show_table(self):
td = TabularData(self.TABLE_COLUMNS, fill_value="-")
conf = self.config.read()["machine"]
if self.args.name:
conf = {self.args.name: conf.get(self.args.name, {})}
self._hide_private(conf)
for machine, machine_config in conf.items():
machine_config["name"] = machine
td.row_from_dict(machine_config)
td.dropna("cols", "all")
td.render()
def run(self):
if self.args.show_origin:
self._show_origin()
else:
self._show_table()
return 0
@@ -104,6 +146,54 @@ class CmdMachineModify(CmdMachineConfig):
return 0
class CmdMachineRename(CmdBase):
def _check_exists(self, conf):
if self.args.name not in conf["machine"]:
raise ConfigError(f"machine '{self.args.name}' doesn't exist.")
def _rename_default(self, conf):
if conf["core"].get("machine") == self.args.name:
conf["core"]["machine"] = self.args.new
def _check_before_rename(self):
from dvc.machine import validate_name
validate_name(self.args.new)
all_config = self.config.load_config_to_level(None)
if self.args.new in all_config.get("machine", {}):
raise ConfigError(
"Rename failed. Machine '{}' already exists.".format(
self.args.new
)
)
ui.write(f"Rename machine '{self.args.name}' to '{self.args.new}'.")
def run(self):
self._check_before_rename()
with self.config.edit(self.args.level) as conf:
self._check_exists(conf)
conf["machine"][self.args.new] = conf["machine"][self.args.name]
try:
self.repo.machine.rename(self.args.name, self.args.new)
except DvcException as error:
del conf["machine"][self.args.new]
raise ConfigError("terraform rename failed") from error
del conf["machine"][self.args.name]
self._rename_default(conf)
up_to_level = self.args.level or "repo"
for level in reversed(self.config.LEVELS):
if level == up_to_level:
break
with self.config.edit(level) as level_conf:
self._rename_default(level_conf)
return 0
class CmdMachineDefault(CmdMachineConfig):
def run(self):
if self.args.name is None and not self.args.unset:
@@ -143,6 +233,64 @@ class CmdMachineCreate(CmdBase):
return 0
class CmdMachineStatus(CmdBase):
INSTANCE_FIELD = ["name", "instance", "status"]
SHOWN_FIELD = [
"cloud",
"instance_ip",
"instance_type",
"instance_hdd_size",
"instance_gpu",
]
FILL_VALUE = "-"
def _add_row(
self,
name: str,
all_status: List[Dict],
td: TabularData,
):
if not all_status:
row = [
name,
self.FILL_VALUE,
"offline",
] # back to `None` after #7167
td.append(row)
for i, status in enumerate(all_status, start=1):
row = [name, f"num_{i}", "running" if status else "offline"]
for field in self.SHOWN_FIELD:
value = str(status.get(field, ""))
row.append(value)
td.append(row)
def run(self):
if self.repo.machine is None:
raise MachineDisabledError
td = TabularData(
self.INSTANCE_FIELD + self.SHOWN_FIELD, fill_value=self.FILL_VALUE
)
if self.args.name:
all_status = list(self.repo.machine.status(self.args.name))
self._add_row(self.args.name, all_status, td)
else:
name_set = set()
for level in self.repo.config.LEVELS:
conf = self.repo.config.read(level)["machine"]
name_set.update(conf.keys())
name_list = list(name_set)
for name in sorted(name_list):
all_status = list(self.repo.machine.status(name))
self._add_row(name, all_status, td)
td.dropna("cols", "all")
td.render()
return 0
class CmdMachineDestroy(CmdBase):
def run(self):
if self.repo.machine is None:
@@ -162,7 +310,7 @@ class CmdMachineSsh(CmdBase):
def add_parser(subparsers, parent_parser):
from dvc.command.config import parent_config_parser
from dvc.commands.config import parent_config_parser
machine_HELP = "Set up and manage cloud machines."
machine_parser = subparsers.add_parser(
@@ -280,6 +428,18 @@ def add_parser(subparsers, parent_parser):
)
machine_modify_parser.set_defaults(func=CmdMachineModify)
machine_RENAME_HELP = "Rename a machine "
machine_rename_parser = machine_subparsers.add_parser(
"rename",
parents=[parent_config_parser, parent_parser],
description=append_doc_link(machine_RENAME_HELP, "remote/rename"),
help=machine_RENAME_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
machine_rename_parser.add_argument("name", help="Machine to be renamed")
machine_rename_parser.add_argument("new", help="New name of the machine")
machine_rename_parser.set_defaults(func=CmdMachineRename)
machine_REMOVE_HELP = "Remove an machine."
machine_remove_parser = machine_subparsers.add_parser(
"remove",
@@ -296,7 +456,7 @@ def add_parser(subparsers, parent_parser):
machine_CREATE_HELP = "Create and start a machine instance."
machine_create_parser = machine_subparsers.add_parser(
"create",
parents=[parent_config_parser, parent_parser],
parents=[parent_parser],
description=append_doc_link(machine_CREATE_HELP, "machine/create"),
help=machine_CREATE_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -306,10 +466,25 @@ def add_parser(subparsers, parent_parser):
)
machine_create_parser.set_defaults(func=CmdMachineCreate)
machine_STATUS_HELP = (
"List the status of running instances for one/all machines."
)
machine_status_parser = machine_subparsers.add_parser(
"status",
parents=[parent_parser],
description=append_doc_link(machine_STATUS_HELP, "machine/status"),
help=machine_STATUS_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
machine_status_parser.add_argument(
"name", nargs="?", help="(optional) Name of the machine."
)
machine_status_parser.set_defaults(func=CmdMachineStatus)
machine_DESTROY_HELP = "Destroy an machine instance."
machine_destroy_parser = machine_subparsers.add_parser(
"destroy",
parents=[parent_config_parser, parent_parser],
parents=[parent_parser],
description=append_doc_link(machine_DESTROY_HELP, "machine/destroy"),
help=machine_DESTROY_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -322,7 +497,7 @@ def add_parser(subparsers, parent_parser):
machine_SSH_HELP = "Connect to a machine via SSH."
machine_ssh_parser = machine_subparsers.add_parser(
"ssh",
parents=[parent_config_parser, parent_parser],
parents=[parent_parser],
description=append_doc_link(machine_SSH_HELP, "machine/ssh"),
help=machine_SSH_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link, fix_subparsers
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link, fix_subparsers
from dvc.exceptions import DvcException
from dvc.ui import ui
from dvc.utils.serialize import encode_exception
@@ -31,16 +32,14 @@ class CmdMetricsShow(CmdMetricsBase):
logger.exception("")
return 1
if self.args.show_json:
import json
ui.write(json.dumps(metrics, default=encode_exception))
if self.args.json:
ui.write_json(metrics, default=encode_exception)
else:
from dvc.compare import show_metrics
show_metrics(
metrics,
markdown=self.args.show_md,
markdown=self.args.markdown,
all_branches=self.args.all_branches,
all_tags=self.args.all_tags,
all_commits=self.args.all_commits,
@@ -65,20 +64,20 @@ class CmdMetricsDiff(CmdMetricsBase):
logger.exception("failed to show metrics diff")
return 1
if self.args.show_json:
import json
ui.write(json.dumps(diff))
if self.args.json:
ui.write_json(diff)
else:
from dvc.compare import show_diff
show_diff(
diff,
title="Metric",
markdown=self.args.show_md,
markdown=self.args.markdown,
no_path=self.args.no_path,
precision=self.args.precision or DEFAULT_PRECISION,
round_digits=True,
a_rev=self.args.a_rev,
b_rev=self.args.b_rev,
)
return 0
@@ -140,15 +139,18 @@ def add_parser(subparsers, parent_parser):
help="Show metrics for all commits.",
)
metrics_show_parser.add_argument(
"--json",
"--show-json",
action="store_true",
default=False,
help="Show output in JSON format.",
)
metrics_show_parser.add_argument(
"--md",
"--show-md",
action="store_true",
default=False,
dest="markdown",
help="Show tabulated output in the Markdown format (GFM).",
)
metrics_show_parser.add_argument(
@@ -220,15 +222,18 @@ def add_parser(subparsers, parent_parser):
help="Show unchanged metrics as well.",
)
metrics_diff_parser.add_argument(
"--json",
"--show-json",
action="store_true",
default=False,
help="Show output in JSON format.",
)
metrics_diff_parser.add_argument(
"--md",
"--show-md",
action="store_true",
default=False,
dest="markdown",
help="Show tabulated output in the Markdown format (GFM).",
)
metrics_diff_parser.add_argument(
+3 -2
View File
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link, fix_subparsers
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link, fix_subparsers
from dvc.exceptions import DvcException
from dvc.ui import ui
@@ -25,19 +26,19 @@ class CmdParamsDiff(CmdBase):
logger.exception("failed to show params diff")
return 1
if self.args.show_json:
import json
ui.write(json.dumps(diff))
if self.args.json:
ui.write_json(diff)
else:
from dvc.compare import show_diff
show_diff(
diff,
title="Param",
markdown=self.args.show_md,
markdown=self.args.markdown,
no_path=self.args.no_path,
show_changes=False,
a_rev=self.args.a_rev,
b_rev=self.args.b_rev,
)
return 0
@@ -103,15 +104,18 @@ def add_parser(subparsers, parent_parser):
help="Show only params that are stage dependencies.",
)
params_diff_parser.add_argument(
"--json",
"--show-json",
action="store_true",
default=False,
help="Show output in JSON format.",
)
params_diff_parser.add_argument(
"--md",
"--show-md",
action="store_true",
default=False,
dest="markdown",
help="Show tabulated output in the Markdown format (GFM).",
)
params_diff_parser.add_argument(
+149 -32
View File
@@ -1,16 +1,29 @@
import argparse
import json
import logging
import os
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link, fix_subparsers
from funcy import first
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link, fix_subparsers
from dvc.exceptions import DvcException
from dvc.render.utils import find_vega, render
from dvc.ui import ui
from dvc.utils import format_link
logger = logging.getLogger(__name__)
def _show_json(renderers, split=False):
from dvc.render.convert import to_json
result = {
renderer.name: to_json(renderer, split) for renderer in renderers
}
ui.write_json(result)
class CmdPlots(CmdBase):
def _func(self, *args, **kwargs):
raise NotImplementedError
@@ -25,6 +38,9 @@ class CmdPlots(CmdBase):
def run(self):
from pathlib import Path
from dvc.render.match import match_renderers
from dvc_render import render_html
if self.args.show_vega:
if not self.args.targets:
logger.error("please specify a target for `--show-vega`")
@@ -34,8 +50,15 @@ class CmdPlots(CmdBase):
"you can only specify one target for `--show-vega`"
)
return 1
if self.args.json:
logger.error(
"'--show-vega' and '--json' are mutually exclusive "
"options."
)
return 1
try:
plots_data = self._func(
targets=self.args.targets, props=self._props()
)
@@ -46,35 +69,59 @@ class CmdPlots(CmdBase):
"visualization file will not be created."
)
if self.args.show_vega:
target = self.args.targets[0]
plot_json = find_vega(self.repo, plots_data, target)
if plot_json:
ui.write(plot_json)
return 0
rel: str = self.args.out or "dvc_plots"
path: Path = (Path.cwd() / rel).resolve()
index_path = render(
self.repo,
plots_data,
path=path,
html_template_path=self.args.html_template,
out: str = self.args.out or self.repo.config.get("plots", {}).get(
"out_dir", "dvc_plots"
)
assert index_path.is_absolute()
url = index_path.as_uri()
ui.write(url)
renderers_out = (
out if self.args.json else os.path.join(out, "static")
)
renderers = match_renderers(
plots_data=plots_data,
out=renderers_out,
templates_dir=self.repo.plots.templates_dir,
)
if self.args.open:
import webbrowser
if self.args.show_vega:
renderer = first(filter(lambda r: r.TYPE == "vega", renderers))
if renderer:
ui.write_json(json.loads(renderer.get_filled_template()))
return 0
if self.args.json:
_show_json(renderers, self.args.split)
return 0
opened = webbrowser.open(index_path)
if not opened:
ui.error_write(
"Failed to open. Please try opening it manually."
html_template_path = self.args.html_template
if not html_template_path:
html_template_path = self.repo.config.get("plots", {}).get(
"html_template", None
)
if html_template_path and not os.path.isabs(
html_template_path
):
html_template_path = os.path.join(
self.repo.dvc_dir, html_template_path
)
return 1
output_file: Path = (Path.cwd() / out).resolve() / "index.html"
render_html(
renderers=renderers,
output_file=output_file,
template_path=html_template_path,
)
ui.write(output_file.as_uri())
auto_open = self.repo.config["plots"].get("auto_open", False)
if self.args.open or auto_open:
if not auto_open:
ui.write(
"To enable auto opening, you can run:\n"
"\n"
"\tdvc config plots.auto_open true"
)
return ui.open_browser(output_file)
return 0
except DvcException:
@@ -109,6 +156,37 @@ class CmdPlotsModify(CmdPlots):
return 0
class CmdPlotsTemplates(CmdBase):
TEMPLATES_CHOICES = [
"simple",
"linear",
"confusion",
"confusion_normalized",
"scatter",
"smooth",
]
def run(self):
from dvc_render.vega_templates import dump_templates
try:
out = (
os.path.join(os.getcwd(), self.args.out)
if self.args.out
else self.repo.plots.templates_dir
)
targets = [self.args.target] if self.args.target else None
dump_templates(output=out, targets=targets)
templates_path = os.path.relpath(out, os.getcwd())
ui.write(f"Templates have been written into '{templates_path}'.")
return 0
except DvcException:
logger.exception("")
return 1
def add_parser(subparsers, parent_parser):
PLOTS_HELP = (
"Commands to visualize and compare plot metrics in structured files "
@@ -145,7 +223,8 @@ def add_parser(subparsers, parent_parser):
"Shows all plots by default.",
).complete = completion.FILE
_add_props_arguments(plots_show_parser)
_add_output_arguments(plots_show_parser)
_add_output_argument(plots_show_parser)
_add_ui_arguments(plots_show_parser)
plots_show_parser.set_defaults(func=CmdPlotsShow)
PLOTS_DIFF_HELP = (
@@ -180,10 +259,14 @@ def add_parser(subparsers, parent_parser):
"revisions", nargs="*", default=None, help="Git commits to plot from"
)
_add_props_arguments(plots_diff_parser)
_add_output_arguments(plots_diff_parser)
_add_output_argument(plots_diff_parser)
_add_ui_arguments(plots_diff_parser)
plots_diff_parser.set_defaults(func=CmdPlotsDiff)
PLOTS_MODIFY_HELP = "Modify display properties of plot metrics files."
PLOTS_MODIFY_HELP = (
"Modify display properties of data-series plots "
"(has no effect on image-type plots)."
)
plots_modify_parser = plots_subparsers.add_parser(
"modify",
parents=[parent_parser],
@@ -203,6 +286,27 @@ def add_parser(subparsers, parent_parser):
)
plots_modify_parser.set_defaults(func=CmdPlotsModify)
TEMPLATES_HELP = (
"Write built-in plots templates to a directory "
"(.dvc/plots by default)."
)
plots_templates_parser = plots_subparsers.add_parser(
"templates",
parents=[parent_parser],
description=append_doc_link(TEMPLATES_HELP, "plots/templates"),
help=TEMPLATES_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
plots_templates_parser.add_argument(
"target",
default=None,
nargs="?",
choices=CmdPlotsTemplates.TEMPLATES_CHOICES,
help="Template to write. Writes all templates by default.",
)
_add_output_argument(plots_templates_parser, typ="templates")
plots_templates_parser.set_defaults(func=CmdPlotsTemplates)
def _add_props_arguments(parser):
parser.add_argument(
@@ -242,20 +346,33 @@ def _add_props_arguments(parser):
)
def _add_output_arguments(parser):
def _add_output_argument(parser, typ="plots"):
parser.add_argument(
"-o",
"--out",
default=None,
help="Destination path to save plots to",
help=f"Directory to save {typ} to.",
metavar="<path>",
).complete = completion.DIR
def _add_ui_arguments(parser):
parser.add_argument(
"--show-vega",
action="store_true",
default=False,
help="Show output in Vega format.",
)
parser.add_argument(
"--json",
"--show-json",
action="store_true",
default=False,
help=argparse.SUPPRESS,
)
parser.add_argument(
"--split", action="store_true", default=False, help=argparse.SUPPRESS
)
parser.add_argument(
"--open",
action="store_true",
@@ -1,7 +1,7 @@
import argparse
from dvc.command.base import append_doc_link, fix_subparsers
from dvc.command.config import CmdConfig
from dvc.cli.utils import append_doc_link, fix_subparsers
from dvc.commands.config import CmdConfig
from dvc.ui import ui
from dvc.utils import format_link
@@ -86,7 +86,7 @@ class CmdRemoteDefault(CmdRemote):
if self.args.name is None and not self.args.unset:
conf = self.config.read(self.args.level)
try:
print(conf["core"]["remote"])
ui.write(conf["core"]["remote"])
except KeyError:
ui.write("No default remote set")
return 1
@@ -113,8 +113,8 @@ class CmdRemoteDefault(CmdRemote):
class CmdRemoteList(CmdRemote):
def run(self):
conf = self.config.read(self.args.level)
for name, conf in conf["remote"].items():
ui.write(name, conf["url"], sep="\t")
for name, remote_conf in conf["remote"].items():
ui.write(name, remote_conf["url"], sep="\t")
return 0
@@ -151,7 +151,7 @@ class CmdRemoteRename(CmdRemote):
def add_parser(subparsers, parent_parser):
from dvc.command.config import parent_config_parser
from dvc.commands.config import parent_config_parser
REMOTE_HELP = "Set up and manage data remotes."
remote_parser = subparsers.add_parser(
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
+58 -50
View File
@@ -1,21 +1,22 @@
import argparse
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.status import CmdDataStatus
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.commands.status import CmdDataStatus
class CmdRepro(CmdBase):
def run(self):
from dvc.ui import ui
stages = self.repo.reproduce(**self._repro_kwargs)
stages = self.repo.reproduce(
**self._common_kwargs, **self._repro_kwargs
)
if len(stages) == 0:
ui.write(CmdDataStatus.UP_TO_DATE_MSG)
else:
ui.write(
"Use `dvc push` to send your updates to " "remote storage."
)
ui.write("Use `dvc push` to send your updates to remote storage.")
if self.args.metrics:
from dvc.compare import show_metrics
@@ -26,7 +27,7 @@ class CmdRepro(CmdBase):
return 0
@property
def _repro_kwargs(self):
def _common_kwargs(self):
return {
"targets": self.args.targets,
"single_item": self.args.single_item,
@@ -35,12 +36,17 @@ class CmdRepro(CmdBase):
"interactive": self.args.interactive,
"pipeline": self.args.pipeline,
"all_pipelines": self.args.all_pipelines,
"run_cache": not self.args.no_run_cache,
"no_commit": self.args.no_commit,
"downstream": self.args.downstream,
"recursive": self.args.recursive,
"force_downstream": self.args.force_downstream,
"pull": self.args.pull,
}
@property
def _repro_kwargs(self):
return {
"run_cache": not self.args.no_run_cache,
"no_commit": self.args.no_commit,
"glob": self.args.glob,
}
@@ -58,6 +64,13 @@ def add_arguments(repro_parser):
default=False,
help="Reproduce even if dependencies were not changed.",
)
repro_parser.add_argument(
"-i",
"--interactive",
action="store_true",
default=False,
help="Ask for confirmation before reproducing each stage.",
)
repro_parser.add_argument(
"-s",
"--single-item",
@@ -66,27 +79,6 @@ def add_arguments(repro_parser):
help="Reproduce only single data item without recursive dependencies "
"check.",
)
repro_parser.add_argument(
"-m",
"--metrics",
action="store_true",
default=False,
help="Show metrics after reproduction.",
)
repro_parser.add_argument(
"--dry",
action="store_true",
default=False,
help="Only print the commands that would be executed without "
"actually executing.",
)
repro_parser.add_argument(
"-i",
"--interactive",
action="store_true",
default=False,
help="Ask for confirmation before reproducing each stage.",
)
repro_parser.add_argument(
"-p",
"--pipeline",
@@ -110,13 +102,17 @@ def add_arguments(repro_parser):
help="Reproduce all stages in the specified directory.",
)
repro_parser.add_argument(
"--no-run-cache",
"-m",
"--metrics",
action="store_true",
default=False,
help=(
"Execute stage commands even if they have already been run with "
"the same command/dependencies/outputs/etc before."
),
help="Show metrics after reproduction.",
)
repro_parser.add_argument(
"--downstream",
action="store_true",
default=False,
help="Start from the specified stages when reproducing pipelines.",
)
repro_parser.add_argument(
"--force-downstream",
@@ -125,18 +121,6 @@ def add_arguments(repro_parser):
help="Reproduce all descendants of a changed stage even if their "
"direct dependencies didn't change.",
)
repro_parser.add_argument(
"--no-commit",
action="store_true",
default=False,
help="Don't put files/directories into cache.",
)
repro_parser.add_argument(
"--downstream",
action="store_true",
default=False,
help="Start from the specified stages when reproducing pipelines.",
)
repro_parser.add_argument(
"--pull",
action="store_true",
@@ -147,10 +131,11 @@ def add_arguments(repro_parser):
),
)
repro_parser.add_argument(
"--glob",
"--dry",
action="store_true",
default=False,
help="Allows targets containing shell-style wildcards.",
help="Only print the commands that would be executed without "
"actually executing.",
)
@@ -165,5 +150,28 @@ def add_parser(subparsers, parent_parser):
help=REPRO_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
# repro/exp run shared args
add_arguments(repro_parser)
# repro only args
repro_parser.add_argument(
"--glob",
action="store_true",
default=False,
help="Allows targets containing shell-style wildcards.",
)
repro_parser.add_argument(
"--no-commit",
action="store_true",
default=False,
help="Don't put files/directories into cache.",
)
repro_parser.add_argument(
"--no-run-cache",
action="store_true",
default=False,
help=(
"Execute stage commands even if they have already been run with "
"the same command/dependencies/outputs/etc before."
),
)
repro_parser.set_defaults(func=CmdRepro)
+2 -1
View File
@@ -1,7 +1,8 @@
import argparse
import logging
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import append_doc_link
from dvc.utils import relpath
logger = logging.getLogger(__name__)
+19 -18
View File
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.stage import parse_cmd
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.commands.stage import parse_cmd
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
@@ -23,7 +24,7 @@ class CmdRun(CmdBase):
self.args.outs_persist_no_cache,
self.args.checkpoints,
self.args.params,
self.args.cmd,
self.args.command,
]
): # pragma: no cover
logger.error(
@@ -36,7 +37,7 @@ class CmdRun(CmdBase):
kwargs = vars(self.args)
kwargs.update(
{
"cmd": parse_cmd(self.args.cmd),
"cmd": parse_cmd(self.args.command),
"fname": kwargs.pop("file"),
"no_exec": (self.args.no_exec or bool(self.args.checkpoints)),
"run_cache": not kwargs.pop("no_run_cache"),
@@ -52,7 +53,7 @@ class CmdRun(CmdBase):
def add_parser(subparsers, parent_parser):
from dvc.command.stage import _add_common_args
from dvc.commands.stage import _add_common_args
RUN_HELP = (
"Generate a dvc.yaml file from a command and execute the command."
@@ -68,12 +69,25 @@ def add_parser(subparsers, parent_parser):
run_parser.add_argument(
"--file", metavar="<filename>", help=argparse.SUPPRESS
)
run_parser.add_argument(
"--single-stage",
action="store_true",
default=False,
help=argparse.SUPPRESS,
)
_add_common_args(run_parser)
run_parser.add_argument(
"--no-exec",
action="store_true",
default=False,
help="Only create dvc.yaml without actually running it.",
)
run_parser.add_argument(
"--no-commit",
action="store_true",
default=False,
help="Don't put files/directories into cache.",
)
run_parser.add_argument(
"--no-run-cache",
action="store_true",
@@ -83,17 +97,4 @@ def add_parser(subparsers, parent_parser):
"with the same command/dependencies/outputs/etc before."
),
)
run_parser.add_argument(
"--no-commit",
action="store_true",
default=False,
help="Don't put files/directories into cache.",
)
run_parser.add_argument(
"--single-stage",
action="store_true",
default=False,
help=argparse.SUPPRESS,
)
_add_common_args(run_parser)
run_parser.set_defaults(func=CmdRun)
+50 -50
View File
@@ -3,8 +3,9 @@ import logging
from itertools import chain, filterfalse
from typing import TYPE_CHECKING, Dict, Iterable, List
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link, fix_subparsers
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link, fix_subparsers
from dvc.utils.cli_parse import parse_params
from dvc.utils.humanize import truncate_text
@@ -93,9 +94,7 @@ class CmdStageList(CmdBase):
self.repo.stage_collection_error_handler = log_error
stages = self._get_stages()
names_only = self.args.names_only
data = prepare_stages_data(stages, description=not names_only)
data = prepare_stages_data(stages, description=not self.args.name_only)
ui.table(data.items())
return 0
@@ -129,7 +128,7 @@ class CmdStageAdd(CmdBase):
kwargs = vars(self.args)
kwargs.update(
{
"cmd": parse_cmd(kwargs.pop("cmd")),
"cmd": parse_cmd(kwargs.pop("command")),
"params": parse_params(self.args.params),
}
)
@@ -138,6 +137,13 @@ class CmdStageAdd(CmdBase):
def _add_common_args(parser):
parser.add_argument(
"-f",
"--force",
action="store_true",
default=False,
help="Overwrite existing stage",
)
parser.add_argument(
"-d",
"--deps",
@@ -146,6 +152,14 @@ def _add_common_args(parser):
help="Declare dependencies for reproducible cmd.",
metavar="<path>",
).complete = completion.FILE
parser.add_argument(
"-p",
"--params",
action="append",
default=[],
help="Declare parameter to use as additional dependency.",
metavar="[<filename>:]<params_list>",
).complete = completion.FILE
parser.add_argument(
"-o",
"--outs",
@@ -164,13 +178,36 @@ def _add_common_args(parser):
metavar="<filename>",
).complete = completion.FILE
parser.add_argument(
"-p",
"--params",
"-c",
"--checkpoints",
action="append",
default=[],
help="Declare parameter to use as additional dependency.",
metavar="[<filename>:]<params_list>",
help="Declare checkpoint output file or directory for 'dvc exp run'. "
"Not compatible with 'dvc repro'.",
metavar="<filename>",
).complete = completion.FILE
parser.add_argument(
"--external",
action="store_true",
default=False,
help="Allow outputs that are outside of the DVC repository.",
)
parser.add_argument(
"--outs-persist",
action="append",
default=[],
help="Declare output file or directory that will not be "
"removed upon repro.",
metavar="<filename>",
)
parser.add_argument(
"--outs-persist-no-cache",
action="append",
default=[],
help="Declare output file or directory that will not be "
"removed upon repro (do not put into DVC cache).",
metavar="<filename>",
)
parser.add_argument(
"-m",
"--metrics",
@@ -213,7 +250,7 @@ def _add_common_args(parser):
"--live-no-summary",
action="store_true",
default=False,
help="Signal dvclive logger to not dump latest metrics file.",
help=argparse.SUPPRESS,
)
parser.add_argument(
"--live-no-html",
@@ -227,50 +264,12 @@ def _add_common_args(parser):
help="Directory within your repo to run your command in.",
metavar="<path>",
)
parser.add_argument(
"-f",
"--force",
action="store_true",
default=False,
help="Overwrite existing stage",
)
parser.add_argument(
"--outs-persist",
action="append",
default=[],
help="Declare output file or directory that will not be "
"removed upon repro.",
metavar="<filename>",
)
parser.add_argument(
"--outs-persist-no-cache",
action="append",
default=[],
help="Declare output file or directory that will not be "
"removed upon repro (do not put into DVC cache).",
metavar="<filename>",
)
parser.add_argument(
"-c",
"--checkpoints",
action="append",
default=[],
help="Declare checkpoint output file or directory for 'dvc exp run'. "
"Not compatible with 'dvc repro'.",
metavar="<filename>",
).complete = completion.FILE
parser.add_argument(
"--always-changed",
action="store_true",
default=False,
help="Always consider this DVC-file as changed.",
)
parser.add_argument(
"--external",
action="store_true",
default=False,
help="Allow outputs that are outside of the DVC repository.",
)
parser.add_argument(
"--desc",
type=str,
@@ -281,7 +280,7 @@ def _add_common_args(parser):
),
)
parser.add_argument(
"cmd",
"command",
nargs=argparse.REMAINDER,
help="Command to execute.",
metavar="command",
@@ -357,6 +356,7 @@ def add_parser(subparsers, parent_parser):
help="List all stages inside the specified directory.",
)
stage_list_parser.add_argument(
"--name-only",
"--names-only",
action="store_true",
default=False,
@@ -1,6 +1,6 @@
import logging
from dvc.command.data_sync import CmdDataBase
from dvc.commands.data_sync import CmdDataBase
from dvc.exceptions import DvcException
from dvc.ui import ui
from dvc.utils import format_link
@@ -69,10 +69,8 @@ class CmdDataStatus(CmdDataBase):
if self.args.quiet:
return bool(st)
if self.args.show_json:
import json
ui.write(json.dumps(st))
if self.args.json:
ui.write_json(st)
return 0
if st:
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
@@ -1,8 +1,9 @@
import argparse
import logging
from dvc.command import completion
from dvc.command.base import CmdBase, append_doc_link
from dvc.cli import completion
from dvc.cli.command import CmdBase
from dvc.cli.utils import append_doc_link
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
@@ -70,7 +71,6 @@ def add_parser(subparsers, parent_parser):
help=(
"Number of jobs to run simultaneously. "
"The default value is 4 * cpu_count(). "
"For SSH remotes, the default is 4. "
),
metavar="<number>",
)
@@ -1,7 +1,8 @@
import argparse
import logging
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.cli.command import CmdBaseNoRepo
from dvc.cli.utils import append_doc_link
from dvc.ui import ui
logger = logging.getLogger(__name__)
+152 -7
View File
@@ -11,7 +11,9 @@ from typing import (
List,
Mapping,
MutableSequence,
Optional,
Sequence,
Set,
Tuple,
Union,
overload,
@@ -20,6 +22,7 @@ from typing import (
from funcy import reraise
if TYPE_CHECKING:
from dvc.types import StrPath
from dvc.ui.table import CellT
@@ -36,11 +39,21 @@ class TabularData(MutableSequence[Sequence["CellT"]]):
self._columns: Dict[str, Column] = {name: Column() for name in columns}
self._keys: List[str] = list(columns)
self._fill_value = fill_value
self._protected: Set[str] = set()
@property
def columns(self) -> List[Column]:
return list(map(self.column, self.keys()))
def is_protected(self, col_name) -> bool:
return col_name in self._protected
def protect(self, *col_names: str):
self._protected.update(col_names)
def unprotect(self, *col_names: str):
self._protected = self._protected.difference(col_names)
def column(self, name: str) -> Column:
return self._columns[name]
@@ -115,6 +128,8 @@ class TabularData(MutableSequence[Sequence["CellT"]]):
del col[item]
def __len__(self) -> int:
if not self._columns:
return 0
return len(self.columns[0])
@property
@@ -122,9 +137,10 @@ class TabularData(MutableSequence[Sequence["CellT"]]):
return len(self.columns), len(self)
def drop(self, *col_names: str) -> None:
for col_name in col_names:
self._keys.remove(col_name)
self._columns.pop(col_name)
for col in col_names:
if not self.is_protected(col):
self._keys.remove(col)
self._columns.pop(col)
def rename(self, from_col_name: str, to_col_name: str) -> None:
self._columns[to_col_name] = self._columns.pop(from_col_name)
@@ -150,6 +166,24 @@ class TabularData(MutableSequence[Sequence["CellT"]]):
writer.writerow(row)
return buff.getvalue()
def to_parallel_coordinates(
self, output_path: "StrPath", color_by: str = None
) -> "StrPath":
from dvc_render.html import render_html
from dvc_render.plotly import ParallelCoordinatesRenderer
render_html(
renderers=[
ParallelCoordinatesRenderer(
self.as_dict(),
color_by=color_by,
fill_value=self._fill_value,
)
],
output_file=output_path,
)
return output_path
def add_column(self, name: str) -> None:
self._columns[name] = Column([self._fill_value] * len(self))
self._keys.append(name)
@@ -168,7 +202,7 @@ class TabularData(MutableSequence[Sequence["CellT"]]):
def render(self, **kwargs: Any):
from dvc.ui import ui
if kwargs.pop("show_csv", False):
if kwargs.pop("csv", False):
ui.write(self.to_csv(), end="")
else:
ui.table(self, headers=self.keys(), **kwargs)
@@ -181,6 +215,109 @@ class TabularData(MutableSequence[Sequence["CellT"]]):
{k: self._columns[k][i] for k in keys} for i in range(len(self))
]
def dropna(
self,
axis: str = "rows",
how="any",
subset: Optional[Iterable[str]] = None,
):
if axis not in ["rows", "cols"]:
raise ValueError(
f"Invalid 'axis' value {axis}."
"Choose one of ['rows', 'cols']"
)
if how not in ["any", "all"]:
raise ValueError(
f"Invalid 'how' value {how}." "Choose one of ['any', 'all']"
)
match_line: Set = set()
match_any = True
if how == "all":
match_any = False
for n_row, row in enumerate(self):
for n_col, col in enumerate(row):
if subset and self.keys()[n_col] not in subset:
continue
if (col == self._fill_value) is match_any:
if axis == "rows":
match_line.add(n_row)
break
else:
match_line.add(self.keys()[n_col])
to_drop = match_line
if how == "all":
if axis == "rows":
to_drop = set(range(len(self)))
else:
to_drop = set(self.keys())
to_drop -= match_line
if axis == "rows":
for name in self.keys():
self._columns[name] = Column(
[
x
for n, x in enumerate(self._columns[name])
if n not in to_drop
]
)
else:
self.drop(*to_drop)
def drop_duplicates(
self,
axis: str = "rows",
subset: Optional[Iterable[str]] = None,
ignore_empty: bool = True,
):
if axis not in ["rows", "cols"]:
raise ValueError(
f"Invalid 'axis' value {axis}."
"Choose one of ['rows', 'cols']"
)
if axis == "cols":
cols_to_drop: List[str] = []
for n_col, col in enumerate(self.columns):
if subset and self.keys()[n_col] not in subset:
continue
# Cast to str because Text is not hashable error
unique_vals = {str(x) for x in col}
if ignore_empty and self._fill_value in unique_vals:
unique_vals -= {self._fill_value}
if len(unique_vals) == 1:
cols_to_drop.append(self.keys()[n_col])
self.drop(*cols_to_drop)
elif axis == "rows":
unique_rows = []
rows_to_drop: List[int] = []
for n_row, row in enumerate(self):
if subset:
row = [
col
for n_col, col in enumerate(row)
if self.keys()[n_col] in subset
]
tuple_row = tuple(row)
if tuple_row in unique_rows:
rows_to_drop.append(n_row)
else:
unique_rows.append(tuple_row)
for name in self.keys():
self._columns[name] = Column(
[
x
for n, x in enumerate(self._columns[name])
if n not in rows_to_drop
]
)
def _normalize_float(val: float, precision: int):
return f"{val:.{precision}g}"
@@ -211,8 +348,12 @@ def diff_table(
precision: int = None,
round_digits: bool = False,
on_empty_diff: str = None,
a_rev: str = None,
b_rev: str = None,
) -> TabularData:
headers: List[str] = ["Path", title, "Old", "New", "Change"]
a_rev = a_rev or "HEAD"
b_rev = b_rev or "workspace"
headers: List[str] = ["Path", title, a_rev, b_rev, "Change"]
fill_value = "-"
td = TabularData(headers, fill_value=fill_value)
@@ -240,8 +381,8 @@ def diff_table(
td.drop("Change")
if not old:
td.drop("Old")
td.rename("New", "Value")
td.drop(a_rev)
td.rename(b_rev, "Value")
return td
@@ -256,6 +397,8 @@ def show_diff(
round_digits: bool = False,
on_empty_diff: str = None,
markdown: bool = False,
a_rev: str = None,
b_rev: str = None,
) -> None:
td = diff_table(
diff,
@@ -266,6 +409,8 @@ def show_diff(
precision=precision,
round_digits=round_digits,
on_empty_diff=on_empty_diff,
a_rev=a_rev,
b_rev=b_rev,
)
td.render(markdown=markdown)
+17 -12
View File
@@ -8,7 +8,6 @@ from functools import partial
from funcy import cached_property, compact, memoize, re_find
from dvc.exceptions import DvcException, NotDvcRepoError
from dvc.path_info import PathInfo
logger = logging.getLogger(__name__)
@@ -87,22 +86,21 @@ class Config(dict):
def __init__(
self, dvc_dir=None, validate=True, fs=None, config=None
): # pylint: disable=super-init-not-called
from dvc.fs.local import LocalFileSystem
from dvc.fs import LocalFileSystem
self.dvc_dir = dvc_dir
self.wfs = LocalFileSystem()
self.fs = fs or self.wfs
if not dvc_dir:
try:
from dvc.repo import Repo
self.dvc_dir = os.path.join(Repo.find_dvc_dir())
self.dvc_dir = Repo.find_dvc_dir()
except NotDvcRepoError:
self.dvc_dir = None
else:
self.dvc_dir = os.path.abspath(os.path.realpath(dvc_dir))
self.wfs = LocalFileSystem(url=self.dvc_dir)
self.fs = fs or self.wfs
self.dvc_dir = self.fs.path.abspath(self.fs.path.realpath(dvc_dir))
self.load(validate=validate, config=config)
@@ -125,8 +123,8 @@ class Config(dict):
}
if self.dvc_dir is not None:
files["repo"] = os.path.join(self.dvc_dir, self.CONFIG)
files["local"] = os.path.join(self.dvc_dir, self.CONFIG_LOCAL)
files["repo"] = self.fs.path.join(self.dvc_dir, self.CONFIG)
files["local"] = self.fs.path.join(self.dvc_dir, self.CONFIG_LOCAL)
return files
@@ -141,7 +139,7 @@ class Config(dict):
dvc.config.Config: config object.
"""
config_file = os.path.join(dvc_dir, Config.CONFIG)
open(config_file, "w+").close()
open(config_file, "w+", encoding="utf-8").close()
return Config(dvc_dir)
def load(self, validate=True, config=None):
@@ -229,6 +227,7 @@ class Config(dict):
@staticmethod
def _to_relpath(conf_dir, path):
from dvc.fs import localfs
from dvc.utils import relpath
from .config_schema import RelPath
@@ -238,8 +237,9 @@ class Config(dict):
if isinstance(path, RelPath) or not os.path.isabs(path):
path = relpath(path, conf_dir)
return localfs.path.as_posix(path)
return PathInfo(path).as_posix()
return path
@staticmethod
def _save_paths(conf, filename):
@@ -265,7 +265,12 @@ class Config(dict):
"key_path": func,
}
},
"machine": {str: {"startup_script": func}},
"machine": {
str: {
"startup_script": func,
"setup_script": func,
}
},
}
return Schema(dirs_schema, extra=ALLOW_EXTRA)(conf)
+39 -7
View File
@@ -79,13 +79,13 @@ REMOTE_COMMON = {
"checksum_jobs": All(Coerce(int), Range(1)),
"jobs": All(Coerce(int), Range(1)),
Optional("no_traverse"): Bool, # obsoleted
"verify": Bool,
}
LOCAL_COMMON = {
"type": supported_cache_type,
Optional("protected", default=False): Bool, # obsoleted
"shared": All(Lower, Choices("group")),
Optional("slow_link_warning", default=True): Bool,
Optional("verify", default=False): Bool,
}
HTTP_COMMON = {
"auth": All(Lower, Choices("basic", "digest", "custom")),
@@ -95,6 +95,7 @@ HTTP_COMMON = {
"ask_password": Bool,
"ssl_verify": Any(Bool, str),
"method": str,
Optional("verify", default=False): Bool,
}
WEBDAV_COMMON = {
"user": str,
@@ -105,6 +106,7 @@ WEBDAV_COMMON = {
"key_path": str,
"timeout": Coerce(int),
"ssl_verify": Any(Bool, str),
Optional("verify", default=False): Bool,
}
SCHEMA = {
@@ -150,17 +152,23 @@ SCHEMA = {
"ssl_verify": Any(Bool, str),
"sse": str,
"sse_kms_key_id": str,
"sse_customer_algorithm": str,
"sse_customer_key": str,
"acl": str,
"grant_read": str,
"grant_read_acp": str,
"grant_write_acp": str,
"grant_full_control": str,
"cache_regions": bool,
"read_timeout": Coerce(int),
"connect_timeout": Coerce(int),
Optional("verify", default=False): Bool,
**REMOTE_COMMON,
},
"gs": {
"projectname": str,
"credentialpath": str,
Optional("verify", default=False): Bool,
**REMOTE_COMMON,
},
"ssh": {
@@ -173,14 +181,18 @@ SCHEMA = {
"timeout": Coerce(int),
"gss_auth": Bool,
"allow_agent": Bool,
Optional("verify", default=False): Bool,
**REMOTE_COMMON,
},
"hdfs": {"user": str, "kerb_ticket": str, **REMOTE_COMMON},
"webhdfs": {
"hdfscli_config": str,
"webhdfs_token": str,
"user": str,
"webhdfs_alias": str,
"kerberos": Bool,
"kerberos_principal": str,
"proxy_to": str,
"ssl_verify": Any(Bool, str),
"token": str,
"use_https": Bool,
Optional("verify", default=False): Bool,
**REMOTE_COMMON,
},
"azure": {
@@ -196,12 +208,14 @@ SCHEMA = {
"exclude_visual_studio_code_credential": Bool,
"exclude_shared_token_cache_credential": Bool,
"exclude_managed_identity_credential": Bool,
Optional("verify", default=False): Bool,
**REMOTE_COMMON,
},
"oss": {
"oss_key_id": str,
"oss_key_secret": str,
"oss_endpoint": str,
Optional("verify", default=True): Bool,
**REMOTE_COMMON,
},
"gdrive": {
@@ -212,6 +226,7 @@ SCHEMA = {
"gdrive_service_account_user_email": str,
"gdrive_service_account_json_file_path": str,
Optional("gdrive_trash_only", default=False): Bool,
Optional("verify", default=True): Bool,
**REMOTE_COMMON,
},
"http": {**HTTP_COMMON, **REMOTE_COMMON},
@@ -223,9 +238,13 @@ SCHEMA = {
)
},
"state": {
"dir": str,
"row_limit": All(Coerce(int), Range(1)), # obsoleted
"row_cleanup_quota": All(Coerce(int), Range(0, 100)), # obsoleted
},
"index": {
"dir": str,
},
"machine": {
str: {
"cloud": All(Lower, Choices("aws", "azure")),
@@ -233,7 +252,6 @@ SCHEMA = {
Lower, Choices("us-west", "us-east", "eu-west", "eu-north")
),
"image": str,
"name": str,
"spot": Bool,
"spot_price": Coerce(float),
"instance_hdd_size": Coerce(int),
@@ -241,6 +259,7 @@ SCHEMA = {
"instance_gpu": Lower,
"ssh_private": str,
"startup_script": str,
"setup_script": str,
},
},
# section for experimental features
@@ -249,5 +268,18 @@ SCHEMA = {
# enabled by default. It's of no use, kept for backward compatibility.
Optional("parametrization", default=True): Bool,
},
"plots": {"html_template": str},
"plots": {
"html_template": str,
Optional("auto_open", default=False): Bool,
"out_dir": str,
},
"exp": {
"code": str,
"data": str,
"models": str,
"metrics": str,
"params": str,
"plots": str,
"live": str,
},
}
+8 -8
View File
@@ -12,8 +12,6 @@ from dvc.utils import fix_env, is_binary
logger = logging.getLogger(__name__)
CREATE_NO_WINDOW = 0x08000000 # only available since Python 3.7 in subprocess.
def _popen(cmd, **kwargs):
prefix = [sys.executable]
@@ -28,6 +26,7 @@ def _popen(cmd, **kwargs):
def _spawn_windows(cmd, env):
from subprocess import (
CREATE_NEW_PROCESS_GROUP,
CREATE_NO_WINDOW,
STARTF_USESHOWWINDOW,
STARTUPINFO,
)
@@ -43,23 +42,23 @@ def _spawn_windows(cmd, env):
def _spawn_posix(cmd, env):
from dvc.main import main
from dvc.cli import main
# NOTE: using os._exit instead of sys.exit, because dvc built
# with PyInstaller has trouble with SystemExit exception and throws
# errors such as "[26338] Failed to execute script __main__"
try:
pid = os.fork()
pid = os.fork() # pylint: disable=no-member
if pid > 0:
return
except OSError:
logger.exception("failed at first fork")
os._exit(1) # pylint: disable=protected-access
os.setsid()
os.setsid() # pylint: disable=no-member
try:
pid = os.fork()
pid = os.fork() # pylint: disable=no-member
if pid > 0:
os._exit(0) # pylint: disable=protected-access
except OSError:
@@ -107,8 +106,9 @@ def daemon(args):
cmd = ["daemon", "-q"] + args
env = fix_env()
file_path = os.path.abspath(inspect.stack()[0][1])
env["PYTHONPATH"] = os.path.dirname(os.path.dirname(file_path))
if not is_binary():
file_path = os.path.abspath(inspect.stack()[0][1])
env["PYTHONPATH"] = os.path.dirname(os.path.dirname(file_path))
env[DVC_DAEMON] = "1"
_spawn(cmd, env)
+48 -24
View File
@@ -3,11 +3,12 @@
import logging
from typing import TYPE_CHECKING, Iterable, Optional
from dvc.objects.db import get_index
from dvc_data.db import get_index
if TYPE_CHECKING:
from dvc.hash_info import HashInfo
from dvc.objects.db.base import ObjectDB
from dvc_data.hashfile.db import HashFileDB
from dvc_data.hashfile.hash_info import HashInfo
from dvc_data.status import CompareStatusResult
logger = logging.getLogger(__name__)
@@ -30,7 +31,7 @@ class DataCloud:
self,
name: Optional[str] = None,
command: str = "<command>",
) -> "ObjectDB":
) -> "HashFileDB":
from dvc.config import NoRemoteError
if not name:
@@ -56,18 +57,44 @@ class DataCloud:
def _init_odb(self, name):
from dvc.fs import get_cloud_fs
from dvc.objects.db import get_odb
from dvc_data.db import get_odb
cls, config, path_info = get_cloud_fs(self.repo, name=name)
config["tmp_dir"] = self.repo.tmp_dir
return get_odb(cls(**config), path_info, **config)
cls, config, fs_path = get_cloud_fs(self.repo, name=name)
config["tmp_dir"] = self.repo.index_db_dir
return get_odb(cls(**config), fs_path, **config)
def _log_missing(self, status: "CompareStatusResult"):
if status.missing:
missing_desc = "\n".join(
f"name: {hash_info.obj_name}, {hash_info}"
for hash_info in status.missing
)
logger.warning(
"Some of the cache files do not exist neither locally "
f"nor on remote. Missing cache files:\n{missing_desc}"
)
def transfer(
self,
src_odb: "HashFileDB",
dest_odb: "HashFileDB",
objs: Iterable["HashInfo"],
**kwargs,
):
from dvc.exceptions import FileTransferError
from dvc_data.transfer import TransferError, transfer
try:
return transfer(src_odb, dest_odb, objs, **kwargs)
except TransferError as exc:
raise FileTransferError(exc.fails) from exc
def push(
self,
objs: Iterable["HashInfo"],
jobs: Optional[int] = None,
remote: Optional[str] = None,
odb: Optional["ObjectDB"] = None,
odb: Optional["HashFileDB"] = None,
):
"""Push data items in a cloud-agnostic way.
@@ -78,17 +105,15 @@ class DataCloud:
By default remote from core.remote config option is used.
odb: optional ODB to push to. Overrides remote.
"""
from dvc.objects.transfer import transfer
if not odb:
odb = self.get_remote_odb(remote, "push")
return transfer(
odb = odb or self.get_remote_odb(remote, "push")
return self.transfer(
self.repo.odb.local,
odb,
objs,
jobs=jobs,
dest_index=get_index(odb),
cache_odb=self.repo.odb.local,
validate_status=self._log_missing,
)
def pull(
@@ -96,7 +121,7 @@ class DataCloud:
objs: Iterable["HashInfo"],
jobs: Optional[int] = None,
remote: Optional[str] = None,
odb: Optional["ObjectDB"] = None,
odb: Optional["HashFileDB"] = None,
):
"""Pull data items in a cloud-agnostic way.
@@ -107,11 +132,8 @@ class DataCloud:
By default remote from core.remote config option is used.
odb: optional ODB to pull from. Overrides remote.
"""
from dvc.objects.transfer import transfer
if not odb:
odb = self.get_remote_odb(remote, "pull")
return transfer(
odb = odb or self.get_remote_odb(remote, "pull")
return self.transfer(
odb,
self.repo.odb.local,
objs,
@@ -119,6 +141,7 @@ class DataCloud:
src_index=get_index(odb),
cache_odb=self.repo.odb.local,
verify=odb.verify,
validate_status=self._log_missing,
)
def status(
@@ -126,7 +149,7 @@ class DataCloud:
objs: Iterable["HashInfo"],
jobs: Optional[int] = None,
remote: Optional[str] = None,
odb: Optional["ObjectDB"] = None,
odb: Optional["HashFileDB"] = None,
log_missing: bool = True,
):
"""Check status of data items in a cloud-agnostic way.
@@ -141,7 +164,7 @@ class DataCloud:
log_missing: log warning messages if file doesn't exist
neither in cache, neither in cloud.
"""
from dvc.objects.status import compare_status
from dvc_data.status import compare_status
if not odb:
odb = self.get_remote_odb(remote, "status")
@@ -156,5 +179,6 @@ class DataCloud:
)
def get_url_for(self, remote, checksum):
remote_odb = self.get_remote_odb(remote)
return str(remote_odb.hash_to_path_info(checksum))
odb = self.get_remote_odb(remote)
path = odb.oid_to_path(checksum)
return odb.fs.unstrip_protocol(path)
+14 -1
View File
@@ -47,16 +47,29 @@ def loads_from(stage, s_list, erepo=None):
def _merge_params(s_list):
d = defaultdict(list)
default_file = ParamsDependency.DEFAULT_PARAMS_FILE
# figure out completely tracked params file, and ignore specific keys
wholly_tracked = set()
for key in s_list:
if not isinstance(key, dict):
continue
wholly_tracked.update(k for k, params in key.items() if not params)
for key in s_list:
if isinstance(key, str):
d[default_file].append(key)
if default_file not in wholly_tracked:
d[default_file].append(key)
continue
if not isinstance(key, dict):
msg = "Only list of str/dict is supported. Got: "
msg += f"'{type(key).__name__}'."
raise ValueError(msg)
for k, params in key.items():
if k in wholly_tracked:
d[k] = []
continue
if not isinstance(params, list):
msg = "Expected list of params for custom params file "
msg += f"'{k}', got '{type(params).__name__}'."
+92 -53
View File
@@ -1,20 +1,33 @@
import logging
import os
import typing
from collections import defaultdict
from typing import Dict
import dpath.util
from voluptuous import Any
from dvc.exceptions import DvcException
from dvc.hash_info import HashInfo
from dvc.utils.serialize import LOADERS, ParseError
from dvc_data.hashfile.hash_info import HashInfo
from .base import Dependency
logger = logging.getLogger(__name__)
class MissingParamsError(DvcException):
pass
class MissingParamsFile(DvcException):
pass
class ParamsIsADirectoryError(DvcException):
pass
class BadParamFileError(DvcException):
pass
@@ -24,51 +37,83 @@ class ParamsDependency(Dependency):
PARAM_SCHEMA = {PARAM_PARAMS: Any(dict, list, None)}
DEFAULT_PARAMS_FILE = "params.yaml"
def __init__(self, stage, path, params):
info = {}
self.params = []
if params:
if isinstance(params, list):
self.params = params
else:
assert isinstance(params, dict)
self.params = list(params.keys())
info = {self.PARAM_PARAMS: params}
super().__init__(
stage,
path
or os.path.join(stage.repo.root_dir, self.DEFAULT_PARAMS_FILE),
info=info,
def __init__(self, stage, path, params=None, repo=None):
self.params = list(params) if params else []
info = (
{self.PARAM_PARAMS: params} if isinstance(params, dict) else None
)
repo = repo or stage.repo
path = path or os.path.join(repo.root_dir, self.DEFAULT_PARAMS_FILE)
super().__init__(stage, path, info=info, repo=repo)
def dumpd(self):
ret = super().dumpd()
if not self.hash_info:
ret[self.PARAM_PARAMS] = self.params
ret[self.PARAM_PARAMS] = self.params or {}
return ret
def fill_values(self, values=None):
"""Load params values dynamically."""
if not values:
if values is None:
return
info = {}
if not self.params:
info.update(values)
for param in self.params:
if param in values:
info[param] = values[param]
self.hash_info = HashInfo(self.PARAM_PARAMS, info)
def workspace_status(self):
status = super().workspace_status()
def read_params(
self, flatten: bool = True, **kwargs: typing.Any
) -> Dict[str, typing.Any]:
try:
config = self.read_file()
except MissingParamsFile:
config = {}
if status.get(str(self)) == "deleted":
return status
if not self.params:
return config
ret = {}
if flatten:
for param in self.params:
try:
ret[param] = dpath.util.get(config, param, separator=".")
except KeyError:
continue
return ret
from dpath.util import merge
for param in self.params:
merge(
ret,
dpath.util.search(config, param, separator="."),
separator=".",
)
return ret
def workspace_status(self):
if not self.exists:
return {str(self): "deleted"}
if self.hash_info.value is None:
return {str(self): "new"}
from funcy import ldistinct
status = defaultdict(dict)
info = self.hash_info.value if self.hash_info else {}
actual = self.read_params()
for param in self.params:
if param not in actual.keys():
# NOTE: we want to preserve the order of params as specified in the
# status. In case of tracking the whole file, the order is top-level
# keys in the file and then the keys in the `info` from `dvc.lock`
# (which are alphabetically sorted).
params = self.params or ldistinct([*actual.keys(), *info.keys()])
for param in params:
if param not in actual:
st = "deleted"
elif param not in info:
st = "new"
@@ -85,42 +130,26 @@ class ParamsDependency(Dependency):
def status(self):
return self.workspace_status()
def _read(self):
def validate_filepath(self):
if not self.exists:
return {}
raise MissingParamsFile(f"Parameters file '{self}' does not exist")
if self.isdir():
raise ParamsIsADirectoryError(
f"'{self}' is a directory, expected a parameters file"
)
suffix = self.path_info.suffix.lower()
loader = LOADERS[suffix]
def read_file(self):
_, ext = os.path.splitext(self.fs_path)
loader = LOADERS[ext]
self.validate_filepath()
try:
return loader(self.path_info, fs=self.repo.fs)
return loader(self.fs_path, fs=self.repo.fs)
except ParseError as exc:
raise BadParamFileError(
f"Unable to read parameters from '{self}'"
) from exc
def read_params_d(self, **kwargs):
config = self._read()
ret = {}
for param in self.params:
dpath.util.merge(
ret,
dpath.util.search(config, param, separator="."),
separator=".",
)
return ret
def read_params(self):
config = self._read()
ret = {}
for param in self.params:
try:
ret[param] = dpath.util.get(config, param, separator=".")
except KeyError:
pass
return ret
def get_hash(self):
info = self.read_params()
@@ -133,3 +162,13 @@ class ParamsDependency(Dependency):
)
return HashInfo(self.PARAM_PARAMS, info)
def save(self):
if not self.exists:
raise self.DoesNotExistError(self)
if not self.isfile and not self.isdir:
raise self.IsNotFileOrDirError(self)
self.ignore()
self.hash_info = self.get_hash()
+28 -29
View File
@@ -5,14 +5,14 @@ from typing import TYPE_CHECKING, Dict, Optional, Set, Tuple
from voluptuous import Required
from dvc.path_info import PathInfo
from dvc.prompt import confirm
from .base import Dependency
if TYPE_CHECKING:
from dvc.hash_info import HashInfo
from dvc.objects.db.base import ObjectDB
from dvc.objects.file import HashFile
from dvc_data.hashfile.file import HashFile
from dvc_data.hashfile.hash_info import HashInfo
from dvc_objects.db import ObjectDB
class RepoDependency(Dependency):
@@ -34,7 +34,7 @@ class RepoDependency(Dependency):
self._staged_objs: Dict[str, "HashFile"] = {}
super().__init__(stage, *args, **kwargs)
def _parse_path(self, fs, path_info):
def _parse_path(self, fs, fs_path):
return None
@property
@@ -63,19 +63,20 @@ class RepoDependency(Dependency):
return {self.PARAM_PATH: self.def_path, self.PARAM_REPO: self.def_repo}
def download(self, to, jobs=None):
from dvc.checkout import checkout
from dvc_data.checkout import checkout
for odb, objs in self.get_used_objs().items():
self.repo.cloud.pull(objs, jobs=jobs, odb=odb)
obj = self.get_obj()
checkout(
to.path_info,
to.fs_path,
to.fs,
obj,
self.repo.odb.local,
dvcignore=None,
ignore=None,
state=self.repo.state,
prompt=confirm,
)
def update(self, rev=None):
@@ -101,8 +102,9 @@ class RepoDependency(Dependency):
) -> Tuple[Dict[Optional["ObjectDB"], Set["HashInfo"]], "HashFile"]:
from dvc.config import NoRemoteError
from dvc.exceptions import NoOutputOrStageError, PathMissingError
from dvc.objects.stage import stage
from dvc.objects.tree import Tree
from dvc.utils import as_posix
from dvc_data.objects.tree import Tree, TreeError
from dvc_data.stage import stage
local_odb = self.repo.odb.local
locked = kwargs.pop("locked", True)
@@ -114,11 +116,10 @@ class RepoDependency(Dependency):
if locked and self.def_repo.get(self.PARAM_REV_LOCK) is None:
self.def_repo[self.PARAM_REV_LOCK] = rev
path_info = PathInfo(repo.root_dir) / str(self.def_path)
if not obj_only:
try:
for odb, obj_ids in repo.used_objs(
[os.fspath(path_info)],
[os.path.join(repo.root_dir, self.def_path)],
force=True,
jobs=kwargs.get("jobs"),
recursive=True,
@@ -132,13 +133,13 @@ class RepoDependency(Dependency):
pass
try:
staging, staged_obj = stage(
staging, _, staged_obj = stage(
local_odb,
path_info,
repo.repo_fs,
as_posix(self.def_path),
repo.dvcfs,
local_odb.fs.PARAM_CHECKSUM,
)
except FileNotFoundError as exc:
except (FileNotFoundError, TreeError) as exc:
raise PathMissingError(
self.def_path, self.def_repo[self.PARAM_URL]
) from exc
@@ -148,45 +149,43 @@ class RepoDependency(Dependency):
self._staged_objs[rev] = staged_obj
used_obj_ids[staging].add(staged_obj.hash_info)
if isinstance(staged_obj, Tree):
used_obj_ids[staging].update(
entry.hash_info for _, entry in staged_obj
)
used_obj_ids[staging].update(oid for _, _, oid in staged_obj)
return used_obj_ids, staged_obj
def _check_circular_import(self, odb, obj_ids):
from dvc.exceptions import CircularImportError
from dvc.fs.repo import RepoFileSystem
from dvc.objects.db.reference import ReferenceObjectDB
from dvc.objects.tree import Tree
from dvc.fs.dvc import DvcFileSystem
from dvc_data.db.reference import ReferenceHashFileDB
from dvc_data.objects.tree import Tree
if not isinstance(odb, ReferenceObjectDB):
if not isinstance(odb, ReferenceHashFileDB):
return
def iter_objs():
for hash_info in obj_ids:
if hash_info.isdir:
tree = Tree.load(odb, hash_info)
yield from (odb.get(entry.hash_info) for _, entry in tree)
yield from (odb.get(hi.value) for _, _, hi in tree)
else:
yield odb.get(hash_info)
yield odb.get(hash_info.value)
checked_urls = set()
for obj in iter_objs():
if not isinstance(obj.fs, RepoFileSystem):
if not isinstance(obj.fs, DvcFileSystem):
continue
if (
obj.fs.repo_url in checked_urls
or obj.fs.root_dir in checked_urls
or obj.fs.repo.root_dir in checked_urls
):
continue
self_url = self.repo.url or self.repo.root_dir
if (
obj.fs.repo_url is not None
and obj.fs.repo_url == self_url
or obj.fs.root_dir == self.repo.root_dir
or obj.fs.repo.root_dir == self.repo.root_dir
):
raise CircularImportError(self, obj.fs.repo_url, self_url)
checked_urls.update([obj.fs.repo_url, obj.fs.root_dir])
checked_urls.update([obj.fs.repo_url, obj.fs.repo.root_dir])
def get_obj(self, filter_info=None, **kwargs):
locked = kwargs.get("locked", True)
+39 -57
View File
@@ -1,10 +1,7 @@
import collections
import contextlib
import logging
import os
from typing import TYPE_CHECKING, Any, Union
from voluptuous import MultipleInvalid
from typing import TYPE_CHECKING, Any, Callable, Tuple, TypeVar, Union
from dvc.exceptions import DvcException
from dvc.parsing.versions import LOCKFILE_VERSION, SCHEMA_KWD
@@ -12,23 +9,18 @@ from dvc.stage import serialize
from dvc.stage.exceptions import (
StageFileBadNameError,
StageFileDoesNotExistError,
StageFileFormatError,
StageFileIsNotDvcFileError,
)
from dvc.types import AnyPath
from dvc.utils import relpath
from dvc.utils.collections import apply_diff
from dvc.utils.serialize import (
dump_yaml,
modify_yaml,
parse_yaml,
parse_yaml_for_update,
)
from dvc.utils.serialize import dump_yaml, modify_yaml
if TYPE_CHECKING:
from dvc.repo import Repo
logger = logging.getLogger(__name__)
_T = TypeVar("_T")
DVC_FILE = "Dvcfile"
DVC_FILE_SUFFIX = ".dvc"
@@ -45,10 +37,6 @@ class FileIsGitIgnored(DvcException):
)
class LockfileCorruptedError(DvcException):
pass
class ParametrizedDumpError(DvcException):
pass
@@ -71,8 +59,8 @@ def is_lock_file(path):
def is_git_ignored(repo, path):
from dvc.fs.local import LocalFileSystem
from dvc.scm.base import NoSCMError
from dvc.fs import LocalFileSystem
from dvc.scm import NoSCMError
try:
return isinstance(repo.fs, LocalFileSystem) and repo.scm.is_ignored(
@@ -96,7 +84,7 @@ def check_dvcfile_path(repo, path):
class FileMixin:
SCHEMA = None
SCHEMA: Callable[[_T], _T]
def __init__(self, repo, path, verify=True, **kwargs):
self.repo = repo
@@ -138,7 +126,11 @@ class FileMixin:
if self._is_git_ignored():
raise FileIsGitIgnored(self.path)
def _load(self):
def load(self, **kwargs: Any) -> Any:
d, _ = self._load(**kwargs)
return d
def _load(self, **kwargs: Any) -> Tuple[Any, str]:
# it raises the proper exceptions by priority:
# 1. when the file doesn't exists
# 2. filename is not a DVC file
@@ -155,18 +147,23 @@ class FileMixin:
raise StageFileIsNotDvcFileError(self.path)
self._check_gitignored()
with self.repo.fs.open(self.path, encoding="utf-8") as fd:
stage_text = fd.read()
d = parse_yaml(stage_text, self.path)
return self.validate(d, self.relpath), stage_text
return self._load_yaml(**kwargs)
@classmethod
def validate(cls, d, fname=None):
assert isinstance(cls.SCHEMA, collections.abc.Callable)
try:
return cls.SCHEMA(d) # pylint: disable=not-callable
except MultipleInvalid as exc:
raise StageFileFormatError(f"'{fname}' format error: {exc}")
def validate(cls, d: _T, fname: str = None) -> _T:
from dvc.utils.strictyaml import validate
return validate(d, cls.SCHEMA, path=fname) # type: ignore[arg-type]
def _load_yaml(self, **kwargs: Any) -> Tuple[Any, str]:
from dvc.utils import strictyaml
return strictyaml.load(
self.path,
self.SCHEMA, # type: ignore[arg-type]
self.repo.fs,
**kwargs,
)
def remove(self, force=False): # pylint: disable=unused-argument
with contextlib.suppress(FileNotFoundError):
@@ -202,7 +199,7 @@ class SingleStageFile(FileMixin):
check_dvcfile_path(self.repo, self.path)
logger.debug(f"Saving information to '{relpath(self.path)}'.")
dump_yaml(self.path, serialize.to_single_stage_file(stage))
self.repo.scm.track_file(self.relpath)
self.repo.scm_context.track_file(self.relpath)
def remove_stage(self, stage): # pylint: disable=unused-argument
self.remove()
@@ -271,7 +268,7 @@ class PipelineFile(FileMixin):
else:
data["stages"].update(stage_data)
self.repo.scm.track_file(self.relpath)
self.repo.scm_context.track_file(self.relpath)
@property
def stage(self):
@@ -298,10 +295,7 @@ class PipelineFile(FileMixin):
if not self.exists():
return
with open(self.path, encoding="utf-8") as f:
d = parse_yaml_for_update(f.read(), self.path)
self.validate(d, self.path)
d, _ = self._load_yaml(round_trip=True)
if stage.name not in d.get("stages", {}):
return
@@ -343,34 +337,25 @@ def migrate_lock_v1_to_v2(d, version_info):
d["stages"] = stages
def lockfile_schema(data: _T) -> _T:
schema = get_lockfile_schema(data)
return schema(data)
class Lockfile(FileMixin):
@classmethod
def validate(cls, d, fname=None):
schema = get_lockfile_schema(d)
try:
return schema(d)
except MultipleInvalid as exc:
raise StageFileFormatError(f"'{fname}' format error: {exc}")
SCHEMA = staticmethod(lockfile_schema) # type: ignore[assignment]
def _verify_filename(self):
pass # lockfile path is hardcoded, so no need to verify here
def _load(self):
def _load(self, **kwargs: Any):
try:
return super()._load()
return super()._load(**kwargs)
except StageFileDoesNotExistError:
# we still need to account for git-ignored dvc.lock file
# even though it may not exist or have been .dvcignored
self._check_gitignored()
return {}, ""
except StageFileFormatError as exc:
raise LockfileCorruptedError(
f"Lockfile '{self.relpath}' is corrupted."
) from exc
def load(self):
d, _ = self._load()
return d
@property
def latest_version_info(self):
@@ -403,16 +388,13 @@ class Lockfile(FileMixin):
data["stages"].update(stage_data)
if modified:
self.repo.scm.track_file(self.relpath)
self.repo.scm_context.track_file(self.relpath)
def remove_stage(self, stage):
if not self.exists():
return
with open(self.path, encoding="utf-8") as f:
d = parse_yaml_for_update(f.read(), self.path)
self.validate(d, self.path)
d, _ = self._load_yaml(round_trip=True)
version = LOCKFILE_VERSION.from_dict(d)
data = d if version == LOCKFILE_VERSION.V1 else d.get("stages", {})
if stage.name not in data:
+15 -38
View File
@@ -1,4 +1,5 @@
"""Exceptions raised by the dvc."""
from dvc.utils import format_link
class DvcException(Exception):
@@ -128,13 +129,6 @@ class NotDvcRepoError(DvcException):
"""Thrown if a directory is not a DVC repo"""
class DvcParserError(DvcException):
"""Base class for CLI parser errors."""
def __init__(self):
super().__init__("parser error")
class CyclicGraphError(DvcException):
def __init__(self, stages):
assert isinstance(stages, list)
@@ -157,9 +151,9 @@ class InitError(DvcException):
class ReproductionError(DvcException):
def __init__(self, dvc_file_name):
self.path = dvc_file_name
super().__init__(f"failed to reproduce '{dvc_file_name}'")
def __init__(self, name):
self.name = name
super().__init__(f"failed to reproduce '{name}'")
class BadMetricError(DvcException):
@@ -208,26 +202,6 @@ class FileMissingError(DvcException):
)
class DvcIgnoreInCollectedDirError(DvcException):
def __init__(self, ignore_dirname):
super().__init__(
".dvcignore file should not be in collected dir path: "
"'{}'".format(ignore_dirname)
)
class GitHookAlreadyExistsError(DvcException):
def __init__(self, hook_name):
from dvc.utils import format_link
super().__init__(
"Hook '{}' already exists. Please refer to {} for more "
"info.".format(
hook_name, format_link("https://man.dvc.org/install")
)
)
class FileTransferError(DvcException):
_METHOD = "transfer"
@@ -305,8 +279,7 @@ class PathMissingError(DvcException):
class RemoteCacheRequiredError(DvcException):
def __init__(self, path_info):
from dvc.utils import format_link
def __init__(self, scheme, fs_path):
super().__init__(
(
@@ -314,8 +287,8 @@ class RemoteCacheRequiredError(DvcException):
"existing cache on '{}' remote. See {} for information on how "
"to set up remote cache."
).format(
path_info,
path_info.scheme,
fs_path,
scheme,
format_link("https://man.dvc.org/config#cache"),
)
)
@@ -342,7 +315,6 @@ class MergeError(DvcException):
class CacheLinkError(DvcException):
from dvc.utils import format_link
SUPPORT_LINK = "See {} for more information.".format(
format_link(
@@ -350,12 +322,12 @@ class CacheLinkError(DvcException):
)
)
def __init__(self, path_infos):
def __init__(self, fs_paths):
msg = "No possible cache link types for '{}'. {}".format(
", ".join([str(path) for path in path_infos]), self.SUPPORT_LINK
", ".join(fs_paths), self.SUPPORT_LINK
)
super().__init__(msg)
self.path_infos = path_infos
self.fs_paths = fs_paths
class CircularImportError(DvcException):
@@ -364,3 +336,8 @@ class CircularImportError(DvcException):
f"'{dep}' contains invalid circular import. "
f"DVC repo '{a}' already imports from '{b}'."
)
class PrettyDvcException(DvcException):
def __pretty_exc__(self, **kwargs):
"""Print prettier exception message."""
+63 -33
View File
@@ -3,7 +3,7 @@ import os
import tempfile
import threading
from contextlib import contextmanager
from typing import Dict
from typing import TYPE_CHECKING, Dict
from funcy import retry, wrap_with
@@ -16,17 +16,24 @@ from dvc.exceptions import (
PathMissingError,
)
from dvc.repo import Repo
from dvc.scm import CloneError, map_scm_exception
from dvc.utils import relpath
if TYPE_CHECKING:
from scmrepo import Git
logger = logging.getLogger(__name__)
@contextmanager
@map_scm_exception()
def external_repo(
url, rev=None, for_write=False, cache_dir=None, cache_types=None, **kwargs
):
from scmrepo.git import Git
from dvc.config import NoRemoteError
from dvc.scm.git import Git
from dvc.fs import GitFileSystem
logger.debug("Creating external repo %s@%s", url, rev)
path = _cached_clone(url, rev, for_write=for_write)
@@ -42,14 +49,22 @@ def external_repo(
config = _get_remote_config(url) if os.path.isdir(url) else {}
config.update(cache_config)
root_dir = path if for_write else os.path.realpath(path)
if for_write:
scm = None
root_dir = path
fs = None
else:
scm = Git(path)
fs = GitFileSystem(scm=scm, rev=rev)
root_dir = "/"
repo_kwargs = dict(
root_dir=root_dir,
url=url,
scm=None if for_write else Git(root_dir),
rev=None if for_write else rev,
fs=fs,
config=config,
repo_factory=erepo_factory(url, cache_config),
repo_factory=erepo_factory(url, root_dir, cache_config),
scm=scm,
**kwargs,
)
@@ -79,14 +94,16 @@ def external_repo(
_remove(path)
def erepo_factory(url, cache_config, *args, **kwargs):
def make_repo(path, **_kwargs):
def erepo_factory(url, root_dir, cache_config):
from dvc.fs import localfs
def make_repo(path, fs=None, **_kwargs):
_config = cache_config.copy()
if os.path.isdir(url):
rel = os.path.relpath(path, _kwargs["scm"].root_dir)
repo_path = os.path.join(url, rel)
fs = fs or localfs
repo_path = os.path.join(url, *fs.path.relparts(path, root_dir))
_config.update(_get_remote_config(repo_path))
return Repo(path, config=_config, **_kwargs)
return Repo(path, fs=fs, config=_config, **_kwargs)
return make_repo
@@ -173,7 +190,7 @@ def _clone_default_branch(url, rev, for_write=False):
The cloned is reactualized with git pull unless rev is a known sha.
"""
from dvc.scm.git import Git
from scmrepo.git import Git
clone_path, shallow = CLONES.get(url, (None, False))
@@ -191,29 +208,35 @@ def _clone_default_branch(url, rev, for_write=False):
# given repo URL it is easier/safer for us to work with
# full clones in this case.
logger.debug("erepo: unshallowing clone for '%s'", url)
_unshallow(git)
_pull(git, unshallow=True)
shallow = False
CLONES[url] = (clone_path, shallow)
else:
logger.debug("erepo: git pull '%s'", url)
git.pull()
_pull(git)
else:
from dvc.scm import clone
logger.debug("erepo: git clone '%s' to a temporary dir", url)
clone_path = tempfile.mkdtemp("dvc-clone")
if not for_write and rev and not Git.is_sha(rev):
# If rev is a tag or branch name try shallow clone first
from dvc.scm.base import CloneError
try:
git = Git.clone(url, clone_path, shallow_branch=rev)
shallow = True
logger.debug(
"erepo: using shallow clone for branch '%s'", rev
git = clone(url, clone_path, shallow_branch=rev)
shallow = os.path.exists(
os.path.join(clone_path, Git.GIT_DIR, "shallow")
)
if shallow:
logger.debug(
"erepo: using shallow clone for branch '%s'", rev
)
except CloneError:
pass
git_dir = os.path.join(clone_path, ".git")
if os.path.exists(git_dir):
_remove(git_dir)
if not git:
git = Git.clone(url, clone_path)
git = clone(url, clone_path)
shallow = False
CLONES[url] = (clone_path, shallow)
finally:
@@ -223,21 +246,28 @@ def _clone_default_branch(url, rev, for_write=False):
return clone_path, shallow
def _unshallow(git):
if git.gitpython.repo.head.is_detached:
# If this is a detached head (i.e. we shallow cloned a tag) switch to
# the default branch
origin_refs = git.gitpython.repo.remotes["origin"].refs
ref = origin_refs["HEAD"].reference
branch_name = ref.name.split("/")[-1]
branch = git.gitpython.repo.create_head(branch_name, ref)
branch.set_tracking_branch(ref)
branch.checkout()
git.pull(unshallow=True)
def _pull(git: "Git", unshallow: bool = False):
from dvc.repo.experiments.utils import fetch_all_exps
git.fetch(unshallow=unshallow)
_merge_upstream(git)
fetch_all_exps(git, "origin")
def _merge_upstream(git: "Git"):
from scmrepo.exceptions import SCMError
try:
branch = git.active_branch()
upstream = f"refs/remotes/origin/{branch}"
if git.get_ref(upstream):
git.merge(upstream)
except SCMError:
pass
def _git_checkout(repo_path, rev):
from dvc.scm.git import Git
from scmrepo.git import Git
logger.debug("erepo: git checkout %s@%s", repo_path, rev)
git = Git(repo_path)
+58 -53
View File
@@ -1,45 +1,50 @@
import posixpath
from urllib.parse import urlparse
from ..scheme import Schemes
from . import utils # noqa: F401
from .azure import AzureFileSystem
from .gdrive import GDriveFileSystem
from .gs import GSFileSystem
from .hdfs import HDFSFileSystem
from .http import HTTPFileSystem
from .https import HTTPSFileSystem
from .local import LocalFileSystem
from .oss import OSSFileSystem
from .s3 import S3FileSystem
from .ssh import SSHFileSystem
from .webdav import WebDAVFileSystem, WebDAVSFileSystem
from .webhdfs import WebHDFSFileSystem
# pylint: disable=unused-import
from dvc_objects.fs import utils # noqa: F401
from dvc_objects.fs import ( # noqa: F401
FS_MAP,
AzureFileSystem,
GDriveFileSystem,
GSFileSystem,
HDFSFileSystem,
HTTPFileSystem,
HTTPSFileSystem,
LocalFileSystem,
MemoryFileSystem,
OSSFileSystem,
S3FileSystem,
Schemes,
SSHFileSystem,
WebDAVFileSystem,
WebDAVSFileSystem,
WebHDFSFileSystem,
generic,
get_fs_cls,
system,
)
from dvc_objects.fs.base import AnyFSPath, FileSystem # noqa: F401
from dvc_objects.fs.errors import ( # noqa: F401
AuthError,
ConfigError,
RemoteMissingDepsError,
)
from dvc_objects.fs.implementations.azure import AzureAuthError # noqa: F401
from dvc_objects.fs.implementations.gdrive import GDriveAuthError # noqa: F401
from dvc_objects.fs.implementations.local import localfs # noqa: F401
from dvc_objects.fs.implementations.ssh import ( # noqa: F401
DEFAULT_PORT as DEFAULT_SSH_PORT,
)
from dvc_objects.fs.path import Path # noqa: F401
FS_MAP = {
Schemes.AZURE: AzureFileSystem,
Schemes.GDRIVE: GDriveFileSystem,
Schemes.GS: GSFileSystem,
Schemes.HDFS: HDFSFileSystem,
Schemes.WEBHDFS: WebHDFSFileSystem,
Schemes.HTTP: HTTPFileSystem,
Schemes.HTTPS: HTTPSFileSystem,
Schemes.S3: S3FileSystem,
Schemes.SSH: SSHFileSystem,
Schemes.OSS: OSSFileSystem,
Schemes.WEBDAV: WebDAVFileSystem,
Schemes.WEBDAVS: WebDAVSFileSystem,
# NOTE: LocalFileSystem is the default
}
from .data import DataFileSystem # noqa: F401
from .dvc import DvcFileSystem # noqa: F401
from .git import GitFileSystem # noqa: F401
# pylint: enable=unused-import
def get_fs_cls(remote_conf, scheme=None):
if not scheme:
scheme = urlparse(remote_conf["url"]).scheme
return FS_MAP.get(scheme, LocalFileSystem)
def get_fs_config(config, **kwargs):
def get_fs_config(repo, config, **kwargs):
name = kwargs.get("name")
if name:
try:
@@ -50,10 +55,10 @@ def get_fs_config(config, **kwargs):
raise RemoteNotFoundError(f"remote '{name}' doesn't exist")
else:
remote_conf = kwargs
return _resolve_remote_refs(config, remote_conf)
return _resolve_remote_refs(repo, config, remote_conf)
def _resolve_remote_refs(config, remote_conf):
def _resolve_remote_refs(repo, config, remote_conf):
# Support for cross referenced remotes.
# This will merge the settings, shadowing base ref with remote_conf.
# For example, having:
@@ -79,28 +84,25 @@ def _resolve_remote_refs(config, remote_conf):
if parsed.scheme != "remote":
return remote_conf
base = get_fs_config(config, name=parsed.netloc)
url = posixpath.join(base["url"], parsed.path.lstrip("/"))
base = get_fs_config(repo, config, name=parsed.netloc)
cls, _, _ = get_cloud_fs(repo, **base)
relpath = parsed.path.lstrip("/").replace("/", cls.sep)
url = cls.sep.join((base["url"], relpath))
return {**base, **remote_conf, "url": url}
def get_cloud_fs(repo, **kwargs):
from dvc.config import ConfigError
from dvc.config import ConfigError as RepoConfigError
from dvc.config_schema import SCHEMA, Invalid
repo_config = repo.config if repo else {}
core_config = repo_config.get("core", {})
remote_conf = get_fs_config(repo_config, **kwargs)
remote_conf = get_fs_config(repo, repo_config, **kwargs)
try:
remote_conf = SCHEMA["remote"][str](remote_conf)
except Invalid as exc:
raise ConfigError(str(exc)) from None
if "jobs" not in remote_conf:
jobs = core_config.get("jobs")
if jobs:
remote_conf["jobs"] = jobs
raise RepoConfigError(str(exc)) from None
if "checksum_jobs" not in remote_conf:
checksum_jobs = core_config.get("checksum_jobs")
@@ -113,10 +115,13 @@ def get_cloud_fs(repo, **kwargs):
remote_conf["gdrive_credentials_tmp_dir"] = repo.tmp_dir
url = remote_conf.pop("url")
path_info = cls.PATH_CLS(
cls._strip_protocol(url) # pylint:disable=protected-access
)
if issubclass(cls, WebDAVFileSystem):
# For WebDAVFileSystem, provided url is the base path itself, so it
# should be treated as being a root path.
fs_path = cls.root_marker
else:
fs_path = cls._strip_protocol(url) # pylint:disable=protected-access
extras = cls._get_kwargs_from_urls(url) # pylint:disable=protected-access
conf = {**extras, **remote_conf} # remote config takes priority
return cls, conf, path_info
return cls, conf, fs_path
-72
View File
@@ -1,72 +0,0 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, List
from dvc.output import Output
from dvc.path_info import PathInfo
if TYPE_CHECKING:
from dvc.repo import Repo
@dataclass
class Metadata:
"""
Container for storing metadata for a given path, similar in spirit to
`os.stat_result`.
"""
# required field
path_info: PathInfo
repo: "Repo"
# computed fields
is_output: bool = field(init=False, default=False) # is it an output?
# is the path part of an output?
part_of_output: bool = field(init=False, default=False)
# does the path contain outputs?
contains_outputs: bool = field(init=False, default=False)
# is the path tracked by dvc? equivalent to (is_output or part_of_output)
is_dvc: bool = field(init=False, default=False)
# does dvc has any output in that path or inside it? equiv. to bool(outs)
output_exists: bool = field(init=False)
# optional fields
isdir: bool = False # is it a directory?
is_exec: bool = False # is it an executable?
outs: List[Output] = field(default_factory=list) # list of outputs
def __post_init__(self):
self.output_exists = bool(self.outs)
if not self.output_exists:
return
# it can contain multiple outputs
if len(self.outs) > 1:
self.contains_outputs = True
else:
out = self.outs[0]
# or, the path itself could be an output
self.is_output = self.path_info == out.path_info
# or, a directory must have an output somewhere deep inside it
if not self.is_output:
self.contains_outputs = out.path_info.isin(self.path_info)
# or, the path could be a part of an output, i.e. inside of an output
self.part_of_output = not self.is_output and not self.contains_outputs
self.is_dvc = self.is_output or self.part_of_output
# if it contains outputs, it must be a dir
self.isdir = self.contains_outputs
@property
def isfile(self):
# self.isdir might require additional computation, especially if it's
# a dir that's inside a dvc-tracked dir
# since, `Metadata()` does not support being used as null (i.e. when
# path does not exist), `isfile` becomes `isdir`'s complement.
return not self.isdir
def __str__(self):
return f"Metadata: {self.path_info}"
-161
View File
@@ -1,161 +0,0 @@
import logging
import os
import threading
from fsspec.asyn import fsspec_loop
from fsspec.utils import infer_storage_options
from funcy import cached_property, memoize, wrap_prop
from dvc.exceptions import DvcException
from dvc.path_info import CloudURLInfo
from dvc.scheme import Schemes
from dvc.utils import format_link
from .fsspec_wrapper import CallbackMixin, ObjectFSWrapper
logger = logging.getLogger(__name__)
_DEFAULT_CREDS_STEPS = (
"https://azuresdkdocs.blob.core.windows.net/$web/python/"
"azure-identity/1.4.0/azure.identity.html#azure.identity"
".DefaultAzureCredential"
)
class AzureAuthError(DvcException):
pass
@memoize
def _az_config():
# NOTE: ideally we would've used get_default_cli().config from
# azure.cli.core, but azure-cli-core has a lot of conflicts with other
# dependencies. So instead we are just use knack directly
from knack.config import CLIConfig
config_dir = os.getenv(
"AZURE_CONFIG_DIR", os.path.expanduser(os.path.join("~", ".azure"))
)
return CLIConfig(config_dir=config_dir, config_env_var_prefix="AZURE")
# pylint:disable=abstract-method
class AzureFileSystem(CallbackMixin, ObjectFSWrapper):
scheme = Schemes.AZURE
PATH_CLS = CloudURLInfo
PARAM_CHECKSUM = "etag"
DETAIL_FIELDS = frozenset(("etag", "size"))
REQUIRES = {
"adlfs": "adlfs",
"knack": "knack",
"azure-identity": "azure.identity",
}
@classmethod
def _strip_protocol(cls, path: str):
bucket = infer_storage_options(path).get("host")
if bucket:
return path
bucket = _az_config().get("storage", "container_name", None)
return f"azure://{bucket}"
@staticmethod
def _get_kwargs_from_urls(urlpath):
ops = infer_storage_options(urlpath)
if "host" in ops:
return {"bucket": ops["host"]}
return {}
def _prepare_credentials(self, **config):
from azure.identity.aio import DefaultAzureCredential
# Disable spam from failed cred types for DefaultAzureCredential
logging.getLogger("azure.identity.aio").setLevel(logging.ERROR)
login_info = {}
login_info["connection_string"] = config.get(
"connection_string",
_az_config().get("storage", "connection_string", None),
)
login_info["account_name"] = config.get(
"account_name", _az_config().get("storage", "account", None)
)
login_info["account_key"] = config.get(
"account_key", _az_config().get("storage", "key", None)
)
login_info["sas_token"] = config.get(
"sas_token", _az_config().get("storage", "sas_token", None)
)
login_info["tenant_id"] = config.get("tenant_id")
login_info["client_id"] = config.get("client_id")
login_info["client_secret"] = config.get("client_secret")
if not (login_info["account_name"] or login_info["connection_string"]):
raise AzureAuthError(
"Authentication to Azure Blob Storage requires either "
"account_name or connection_string.\nLearn more about "
"configuration settings at "
+ format_link("https://man.dvc.org/remote/modify")
)
any_secondary = any(
value for key, value in login_info.items() if key != "account_name"
)
if (
login_info["account_name"]
and not any_secondary
and not config.get("allow_anonymous_login", False)
):
with fsspec_loop():
login_info["credential"] = DefaultAzureCredential(
exclude_interactive_browser_credential=False,
exclude_environment_credential=config.get(
"exclude_environment_credential", False
),
exclude_visual_studio_code_credential=config.get(
"exclude_visual_studio_code_credential", False
),
exclude_shared_token_cache_credential=config.get(
"exclude_shared_token_cache_credential", False
),
exclude_managed_identity_credential=config.get(
"exclude_managed_identity_credential", False
),
)
for login_method, required_keys in [ # noqa
("connection string", ["connection_string"]),
(
"AD service principal",
["tenant_id", "client_id", "client_secret"],
),
("account key", ["account_name", "account_key"]),
("SAS token", ["account_name", "sas_token"]),
(
f"default credentials ({_DEFAULT_CREDS_STEPS})",
["account_name", "credential"],
),
("anonymous login", ["account_name"]),
]:
if all(login_info.get(key) is not None for key in required_keys):
break
else:
login_method = None
self.login_method = login_method
return login_info
@wrap_prop(threading.Lock())
@cached_property
def fs(self):
from adlfs import AzureBlobFileSystem
from azure.core.exceptions import AzureError
try:
return AzureBlobFileSystem(**self.fs_args)
except (ValueError, AzureError) as e:
raise AzureAuthError(
f"Authentication to Azure Blob Storage via {self.login_method}"
" failed.\nLearn more about configuration settings at"
f" {format_link('https://man.dvc.org/remote/modify')}"
) from e
-392
View File
@@ -1,392 +0,0 @@
import contextlib
import logging
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
from functools import partialmethod
from multiprocessing import cpu_count
from typing import Any, ClassVar, Dict, FrozenSet, Optional
from funcy import cached_property
from tqdm.utils import CallbackIOWrapper
from dvc.exceptions import DvcException
from dvc.path_info import URLInfo
from dvc.progress import DEFAULT_CALLBACK, FsspecCallback
from dvc.ui import ui
from dvc.utils import tmp_fname
from dvc.utils.fs import makedirs, move
logger = logging.getLogger(__name__)
class RemoteCmdError(DvcException):
def __init__(self, remote, cmd, ret, err):
super().__init__(
"{remote} command '{cmd}' finished with non-zero return code"
" {ret}': {err}".format(remote=remote, cmd=cmd, ret=ret, err=err)
)
class RemoteActionNotImplemented(DvcException):
def __init__(self, action, scheme):
m = f"{action} is not supported for {scheme} remotes"
super().__init__(m)
class RemoteMissingDepsError(DvcException):
pass
class BaseFileSystem:
sep = "/"
scheme = "base"
REQUIRES: ClassVar[Dict[str, str]] = {}
PATH_CLS = URLInfo # type: Any
_JOBS = 4 * cpu_count()
CHECKSUM_DIR_SUFFIX = ".dir"
HASH_JOBS = max(1, min(4, cpu_count() // 2))
LIST_OBJECT_PAGE_SIZE = 1000
TRAVERSE_WEIGHT_MULTIPLIER = 5
TRAVERSE_PREFIX_LEN = 3
TRAVERSE_THRESHOLD_SIZE = 500000
CAN_TRAVERSE = True
# Needed for some providers, and http open()
CHUNK_SIZE = 64 * 1024 * 1024 # 64 MiB
PARAM_CHECKSUM: ClassVar[Optional[str]] = None
DETAIL_FIELDS: FrozenSet[str] = frozenset()
def __init__(self, **kwargs):
self._check_requires(**kwargs)
self.jobs = kwargs.get("jobs") or self._JOBS
self.hash_jobs = kwargs.get("checksum_jobs") or self.HASH_JOBS
self._config = kwargs
@property
def config(self):
return self._config
@classmethod
def _strip_protocol(cls, path: str):
return path
@staticmethod
def _get_kwargs_from_urls(urlpath): # pylint:disable=unused-argument
return {}
@classmethod
def get_missing_deps(cls):
import importlib
missing = []
for package, module in cls.REQUIRES.items():
try:
importlib.import_module(module)
except ImportError:
missing.append(package)
return missing
def _check_requires(self, **kwargs):
from ..scheme import Schemes
from ..utils import format_link
from ..utils.pkg import PKG
missing = self.get_missing_deps()
if not missing:
return
url = kwargs.get("url", f"{self.scheme}://")
scheme = self.scheme
if scheme == Schemes.WEBDAVS:
scheme = Schemes.WEBDAV
by_pkg = {
"pip": f"pip install 'dvc[{scheme}]'",
"conda": f"conda install -c conda-forge dvc-{scheme}",
}
cmd = by_pkg.get(PKG)
if cmd:
link = format_link("https://dvc.org/doc/install")
hint = (
f"To install dvc with those dependencies, run:\n"
"\n"
f"\t{cmd}\n"
"\n"
f"See {link} for more info."
)
else:
link = format_link("https://github.com/iterative/dvc/issues")
hint = f"Please report this bug to {link}. Thank you!"
raise RemoteMissingDepsError(
f"URL '{url}' is supported but requires these missing "
f"dependencies: {missing}. {hint}"
)
def checksum(self, path_info) -> str:
raise NotImplementedError
def open(self, path_info, mode: str = "r", encoding: str = None, **kwargs):
raise RemoteActionNotImplemented("open", self.scheme)
def exists(self, path_info) -> bool:
raise NotImplementedError
# pylint: disable=unused-argument
def isdir(self, path_info):
"""Optional: Overwrite only if the remote has a way to distinguish
between a directory and a file.
"""
return False
def isfile(self, path_info):
"""Optional: Overwrite only if the remote has a way to distinguish
between a directory and a file.
"""
return True
def isexec(self, path_info):
"""Optional: Overwrite only if the remote has a way to distinguish
between executable and non-executable file.
"""
return False
def iscopy(self, path_info):
"""Check if this file is an independent copy."""
return False # We can't be sure by default
def walk(self, top, topdown=True, onerror=None, **kwargs):
"""Return a generator with (root, dirs, files)."""
raise NotImplementedError
def walk_files(self, path_info, **kwargs):
"""Return a generator with `PathInfo`s to all the files.
Optional kwargs:
prefix (bool): If true `path_info` will be treated as a prefix
rather than directory path.
"""
raise NotImplementedError
def ls(self, path_info, detail=False):
raise RemoteActionNotImplemented("ls", self.scheme)
def find(self, path_info, detail=False, prefix=None):
raise RemoteActionNotImplemented("find", self.scheme)
def is_empty(self, path_info):
return False
def info(self, path_info):
raise NotImplementedError
def getsize(self, path_info):
return self.info(path_info).get("size")
def remove(self, path_info):
raise RemoteActionNotImplemented("remove", self.scheme)
def makedirs(self, path_info, **kwargs):
"""Optional: Implement only if the remote needs to create
directories before copying/linking/moving data
"""
def move(self, from_info, to_info):
self.copy(from_info, to_info)
self.remove(from_info)
def copy(self, from_info, to_info):
raise RemoteActionNotImplemented("copy", self.scheme)
def symlink(self, from_info, to_info):
raise RemoteActionNotImplemented("symlink", self.scheme)
def hardlink(self, from_info, to_info):
raise RemoteActionNotImplemented("hardlink", self.scheme)
def reflink(self, from_info, to_info):
raise RemoteActionNotImplemented("reflink", self.scheme)
# pylint: enable=unused-argument
@classmethod
def is_dir_hash(cls, hash_):
if not hash_:
return False
return hash_.endswith(cls.CHECKSUM_DIR_SUFFIX)
@cached_property
def _local_fs(self):
from dvc.fs import LocalFileSystem
return LocalFileSystem()
def upload(
self,
from_info,
to_info,
total=None,
desc=None,
callback=None,
no_progress_bar=False,
**pbar_args,
):
is_file_obj = hasattr(from_info, "read")
method = "upload_fobj" if is_file_obj else "put_file"
if not hasattr(self, method):
raise RemoteActionNotImplemented(method, self.scheme)
if to_info.scheme != self.scheme:
raise NotImplementedError
if not is_file_obj:
desc = desc or from_info.name
stack = contextlib.ExitStack()
if not callback:
pbar = ui.progress(
desc=desc,
disable=no_progress_bar,
bytes=True,
total=total or -1,
**pbar_args,
)
stack.enter_context(pbar)
callback = pbar.as_callback(self._local_fs, from_info)
if total:
callback.set_size(total)
with stack:
if is_file_obj:
wrapped = CallbackIOWrapper(
callback.relative_update, from_info
)
# `size` is used to provide hints to the WebdavFileSystem
# for legacy servers.
# pylint: disable=no-member
return self.upload_fobj(wrapped, to_info, size=total)
if from_info.scheme != "local":
raise NotImplementedError
logger.debug("Uploading '%s' to '%s'", from_info, to_info)
# pylint: disable=no-member
return self.put_file(
os.fspath(from_info), to_info, callback=callback
)
def download(
self,
from_info,
to_info,
name=None,
callback=None,
no_progress_bar=False,
jobs=None,
_only_file=False,
**kwargs,
):
if not hasattr(self, "get_file"):
raise RemoteActionNotImplemented("get_file", self.scheme)
if from_info.scheme != self.scheme:
raise NotImplementedError
if to_info.scheme != "local":
raise NotImplementedError
download_dir = not _only_file and self.isdir(from_info)
desc = name or to_info.name
stack = contextlib.ExitStack()
if not callback:
pbar_kwargs = {"unit": "Files"} if download_dir else {}
pbar = ui.progress(
total=-1,
desc="Downloading directory" if download_dir else desc,
bytes=not download_dir,
disable=no_progress_bar,
**pbar_kwargs,
)
stack.enter_context(pbar)
callback = pbar.as_callback(self, from_info)
with stack:
if download_dir:
return self._download_dir(
from_info, to_info, callback=callback, jobs=jobs, **kwargs
)
return self._download_file(from_info, to_info, callback=callback)
download_file = partialmethod(download, _only_file=True)
def _download_dir(
self,
from_info,
to_info,
callback=DEFAULT_CALLBACK,
jobs=None,
**kwargs,
):
from_infos = list(self.walk_files(from_info, **kwargs))
if not from_infos:
makedirs(to_info, exist_ok=True)
return None
to_infos = (
to_info / info.relative_to(from_info) for info in from_infos
)
callback.set_size(len(from_infos))
download_files = FsspecCallback.wrap_fn(callback, self._download_file)
max_workers = jobs or self.jobs
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [
executor.submit(download_files, from_info, to_info)
for from_info, to_info in zip(from_infos, to_infos)
]
# NOTE: unlike pulling/fetching cache, where we need to
# download everything we can, not raising an error here might
# turn very ugly, as the user might think that he has
# downloaded a complete directory, while having a partial one,
# which might cause unexpected results in his pipeline.
for future in as_completed(futures):
# NOTE: executor won't let us raise until all futures that
# it has are finished, so we need to cancel them ourselves
# before re-raising.
exc = future.exception()
if exc:
for entry in futures:
entry.cancel()
raise exc
def _download_file(
self,
from_info,
to_info,
callback=DEFAULT_CALLBACK,
):
makedirs(to_info.parent, exist_ok=True)
tmp_file = tmp_fname(to_info)
logger.debug("Downloading '%s' to '%s'", from_info, to_info)
try:
# noqa, pylint: disable=no-member
self.get_file(from_info, tmp_file, callback=callback)
except Exception: # pylint: disable=broad-except
# do we need to rollback makedirs for previously not-existing
# directories?
with contextlib.suppress(FileNotFoundError):
os.unlink(tmp_file)
raise
move(tmp_file, to_info)
+79
View File
@@ -0,0 +1,79 @@
# pylint: disable=unused-import
from contextlib import ExitStack
from typing import TYPE_CHECKING, Optional
from funcy import cached_property
from dvc_objects.fs.callbacks import ( # noqa: F401
DEFAULT_CALLBACK,
Callback,
TqdmCallback,
)
if TYPE_CHECKING:
from dvc.ui._rich_progress import RichTransferProgress
class RichCallback(Callback):
def __init__(
self,
size: Optional[int] = None,
value: int = 0,
progress: "RichTransferProgress" = None,
desc: str = None,
bytes: bool = False, # pylint: disable=redefined-builtin
unit: str = None,
disable: bool = False,
) -> None:
self._progress = progress
self.disable = disable
self._task_kwargs = {
"description": desc or "",
"bytes": bytes,
"unit": unit,
"total": size or 0,
"visible": False,
"progress_type": None if bytes else "summary",
}
self._stack = ExitStack()
super().__init__(size=size, value=value)
@cached_property
def progress(self):
from dvc.ui import ui
from dvc.ui._rich_progress import RichTransferProgress
if self._progress is not None:
return self._progress
progress = RichTransferProgress(
transient=True,
disable=self.disable,
console=ui.error_console,
)
return self._stack.enter_context(progress)
@cached_property
def task(self):
return self.progress.add_task(**self._task_kwargs)
def __enter__(self):
return self
def close(self):
self.progress.clear_task(self.task)
self._stack.close()
def call(self, hook_name=None, **kwargs):
self.progress.update(
self.task,
completed=self.value,
total=self.size,
visible=not self.disable,
)
def branch(self, path_1, path_2, kwargs, child: Optional[Callback] = None):
child = child or RichCallback(
progress=self.progress, desc=path_1, bytes=True
)
return super().branch(path_1, path_2, kwargs, child=child)
+199
View File
@@ -0,0 +1,199 @@
import logging
import threading
import typing
from fsspec import AbstractFileSystem
from funcy import cached_property, wrap_prop
from dvc_objects.fs.base import FileSystem
from dvc_objects.fs.callbacks import DEFAULT_CALLBACK
if typing.TYPE_CHECKING:
from dvc.types import AnyPath
logger = logging.getLogger(__name__)
class _DataFileSystem(AbstractFileSystem): # pylint:disable=abstract-method
"""DVC repo fs.
Args:
repo: DVC repo.
"""
root_marker = "/"
def __init__(self, workspace=None, **kwargs):
super().__init__(**kwargs)
self.repo = kwargs["repo"]
self.workspace = workspace or "repo"
@cached_property
def path(self):
from . import Path
def _getcwd():
return self.root_marker
return Path(self.sep, getcwd=_getcwd)
@property
def config(self):
raise NotImplementedError
def _get_key(self, path):
if self.workspace != "repo":
from . import get_cloud_fs
cls, kwargs, fs_path = get_cloud_fs(None, url=path)
fs = cls(**kwargs)
return (self.workspace, *fs.path.parts(fs_path))
path = self.path.abspath(path)
if path == self.root_marker:
return (self.workspace,)
key = self.path.relparts(path, self.root_marker)
if key == (".") or key == (""):
key = ()
return (self.workspace, *key)
def _get_fs_path(self, path: "AnyPath", remote=None):
from dvc.config import NoRemoteError
info = self.info(path)
if info["type"] == "directory":
raise IsADirectoryError
value = info.get("md5")
if not value:
raise FileNotFoundError
cache_path = self.repo.odb.local.oid_to_path(value)
if self.repo.odb.local.fs.exists(cache_path):
return self.repo.odb.local.fs, cache_path
try:
remote_odb = self.repo.cloud.get_remote_odb(remote)
except NoRemoteError as exc:
raise FileNotFoundError from exc
remote_fs_path = remote_odb.oid_to_path(value)
return remote_odb.fs, remote_fs_path
def open( # type: ignore
self, path: str, mode="r", encoding=None, **kwargs
): # pylint: disable=arguments-renamed, arguments-differ
fs, fspath = self._get_fs_path(path, **kwargs)
return fs.open(fspath, mode=mode, encoding=encoding)
def ls(self, path, detail=True, **kwargs):
info = self.info(path)
if info["type"] != "directory":
return [info] if detail else [path]
root_key = self._get_key(path)
try:
entries = [
self.sep.join((path, name)) if path else name
for name in self.repo.index.tree.ls(prefix=root_key)
]
except KeyError as exc:
raise FileNotFoundError from exc
if not detail:
return entries
return [self.info(epath) for epath in entries]
def isdvc(self, path, recursive=False, strict=True):
try:
info = self.info(path)
except FileNotFoundError:
return False
recurse = recursive or not strict
return bool(info.get("outs") if recurse else info.get("isout"))
def info(self, path, **kwargs):
from dvc_data.hashfile.meta import Meta
key = self._get_key(path)
try:
outs = list(self.repo.index.tree.iteritems(key)) # noqa: B301
except KeyError as exc:
raise FileNotFoundError from exc
ret = {
"type": "file",
"size": 0,
"isexec": False,
"isdvc": False,
"outs": outs,
"name": path,
}
if len(outs) > 1 and outs[0][0] != key:
shortest = self.repo.index.tree.shortest_prefix(key)
if shortest:
assert shortest[1][1].isdir
if len(shortest[0]) <= len(key):
ret["isdvc"] = True
ret["type"] = "directory"
return ret
item_key, (meta, hash_info) = outs[0]
meta = meta or Meta()
if key != item_key:
assert item_key[: len(key)] == key
ret["type"] = "directory"
return ret
ret["size"] = meta.size
ret["isexec"] = meta.isexec
ret[hash_info.name] = hash_info.value
ret["isdvc"] = True
ret["isout"] = True
ret["meta"] = meta
if hash_info and hash_info.isdir:
ret["type"] = "directory"
return ret
def get_file( # pylint: disable=arguments-differ
self, rpath, lpath, callback=DEFAULT_CALLBACK, **kwargs
):
fs, path = self._get_fs_path(rpath)
fs.get_file(path, lpath, callback=callback, **kwargs)
def checksum(self, path):
info = self.info(path)
md5 = info.get("md5")
if md5:
return md5
raise NotImplementedError
class DataFileSystem(FileSystem):
protocol = "local"
PARAM_CHECKSUM = "md5"
def _prepare_credentials(self, **config):
return config
@wrap_prop(threading.Lock())
@cached_property
def fs(self):
return _DataFileSystem(**self.fs_args)
def isdvc(self, path, **kwargs):
return self.fs.isdvc(path, **kwargs)
@property
def repo(self):
return self.fs.repo
+448 -241
View File
@@ -1,273 +1,480 @@
import logging
import ntpath
import os
import typing
import posixpath
import threading
from contextlib import suppress
from itertools import takewhile
from typing import TYPE_CHECKING, Callable, Optional, Tuple, Type, Union
from dvc.exceptions import OutputNotFoundError
from dvc.path_info import PathInfo
from dvc.utils import relpath
from fsspec.spec import AbstractFileSystem
from funcy import cached_property, wrap_prop, wrap_with
from ..progress import DEFAULT_CALLBACK
from ._metadata import Metadata
from .base import BaseFileSystem
from dvc_objects.fs.base import FileSystem
from dvc_objects.fs.callbacks import DEFAULT_CALLBACK
from dvc_objects.fs.path import Path
if typing.TYPE_CHECKING:
from dvc.output import Output
from .data import DataFileSystem
if TYPE_CHECKING:
from dvc.repo import Repo
logger = logging.getLogger(__name__)
RepoFactory = Union[Callable[[str], "Repo"], Type["Repo"]]
class DvcFileSystem(BaseFileSystem): # pylint:disable=abstract-method
"""DVC repo fs.
def as_posix(path: str) -> str:
return path.replace(ntpath.sep, posixpath.sep)
def _wrap_walk(dvc_fs, *args, **kwargs):
for root, dnames, fnames in dvc_fs.walk(*args, **kwargs):
yield dvc_fs.path.join(dvc_fs.repo.root_dir, root), dnames, fnames
def _ls(fs, path):
dnames = []
fnames = []
for entry in fs.ls(path, detail=True):
name = fs.path.name(entry["name"])
if entry["type"] == "directory":
dnames.append(name)
else:
fnames.append(name)
return dnames, fnames
def _merge_info(repo, fs_info, dvc_info):
from . import utils
ret = {"repo": repo}
if dvc_info:
ret["dvc_info"] = dvc_info
ret["type"] = dvc_info["type"]
ret["size"] = dvc_info["size"]
if not fs_info and "md5" in dvc_info:
ret["md5"] = dvc_info["md5"]
if fs_info:
ret["type"] = fs_info["type"]
ret["size"] = fs_info["size"]
isexec = False
if fs_info["type"] == "file":
isexec = utils.is_exec(fs_info["mode"])
ret["isexec"] = isexec
return ret
class _DvcFileSystem(AbstractFileSystem): # pylint:disable=abstract-method
"""DVC + git-tracked files fs.
Args:
repo: DVC repo.
repo: DVC or git repo.
subrepos: traverse to subrepos (by default, it ignores subrepos)
repo_factory: A function to initialize subrepo with, default is Repo.
kwargs: Additional keyword arguments passed to the `DataFileSystem()`.
"""
sep = os.sep
root_marker = "/"
scheme = "local"
PARAM_CHECKSUM = "md5"
PARAM_REPO_URL = "repo_url"
PARAM_REPO_ROOT = "repo_root"
PARAM_REV = "rev"
PARAM_CACHE_DIR = "cache_dir"
PARAM_CACHE_TYPES = "cache_types"
PARAM_SUBREPOS = "subrepos"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.repo = kwargs["repo"]
def __init__(
self,
repo: Optional["Repo"] = None,
subrepos=False,
repo_factory: RepoFactory = None,
**kwargs,
):
super().__init__()
from pygtrie import Trie
if repo is None:
repo, repo_factory = self._repo_from_fs_config(
subrepos=subrepos, **kwargs
)
if not repo_factory:
from dvc.repo import Repo
self.repo_factory: RepoFactory = Repo
else:
self.repo_factory = repo_factory
def _getcwd():
relparts = ()
if repo.fs.path.isin(repo.fs.path.getcwd(), repo.root_dir):
relparts = repo.fs.path.relparts(
repo.fs.path.getcwd(), repo.root_dir
)
return self.root_marker + self.sep.join(relparts)
self.path = Path(self.sep, getcwd=_getcwd)
self.repo = repo
self.hash_jobs = repo.fs.hash_jobs
self._traverse_subrepos = subrepos
self._subrepos_trie = Trie()
"""Keeps track of each and every path with the corresponding repo."""
key = self._get_key(self.repo.root_dir)
self._subrepos_trie[key] = repo
self._datafss = {}
"""Keep a datafs instance of each repo."""
if hasattr(repo, "dvc_dir"):
self._datafss[key] = DataFileSystem(repo=repo)
def _get_key(self, path):
parts = self.repo.fs.path.relparts(path, self.repo.root_dir)
if parts == (".",):
parts = ()
return parts
@property
def repo_url(self):
if self.repo is None:
return None
return self.repo.url
@property
def config(self):
raise NotImplementedError
return {
self.PARAM_REPO_URL: self.repo_url,
self.PARAM_REPO_ROOT: self.repo.root_dir,
self.PARAM_REV: getattr(self.repo.fs, "rev", None),
self.PARAM_CACHE_DIR: os.path.abspath(
self.repo.odb.local.cache_dir
),
self.PARAM_CACHE_TYPES: self.repo.odb.local.cache_types,
self.PARAM_SUBREPOS: self._traverse_subrepos,
}
def _find_outs(self, path, *args, **kwargs):
outs = self.repo.find_outs_by_path(path, *args, **kwargs)
@classmethod
def _repo_from_fs_config(
cls, **config
) -> Tuple["Repo", Optional["RepoFactory"]]:
from dvc.external_repo import erepo_factory, external_repo
from dvc.repo import Repo
def _is_cached(out):
return out.use_cache
url = config.get(cls.PARAM_REPO_URL)
root = config.get(cls.PARAM_REPO_ROOT)
assert url or root
outs = list(filter(_is_cached, outs))
if not outs:
raise OutputNotFoundError(path, self.repo)
def _open(*args, **kwargs):
# NOTE: if original repo was an erepo (and has a URL),
# we cannot use Repo.open() since it will skip erepo
# cache/remote setup for local URLs
if url is None:
return Repo.open(*args, **kwargs)
return external_repo(*args, **kwargs)
return outs
def _get_granular_hash(
self, path_info: PathInfo, out: "Output", remote=None
):
assert isinstance(path_info, PathInfo)
# NOTE: use string paths here for performance reasons
key = tuple(relpath(path_info, out.path_info).split(os.sep))
out.get_dir_cache(remote=remote)
if out.obj is None:
raise FileNotFoundError
obj = out.obj.trie.get(key)
if obj:
return obj.hash_info
raise FileNotFoundError
def _get_fs_path(self, path: PathInfo, remote=None):
try:
outs = self._find_outs(path, strict=False)
except OutputNotFoundError as exc:
raise FileNotFoundError from exc
if len(outs) != 1 or (
outs[0].is_dir_checksum and path == outs[0].path_info
):
raise IsADirectoryError
out = outs[0]
if not out.hash_info:
raise FileNotFoundError
if out.changed_cache(filter_info=path):
from dvc.config import NoRemoteError
try:
remote_odb = self.repo.cloud.get_remote_odb(remote)
except NoRemoteError as exc:
raise FileNotFoundError from exc
if out.is_dir_checksum:
checksum = self._get_granular_hash(path, out).value
else:
checksum = out.hash_info.value
remote_info = remote_odb.hash_to_path_info(checksum)
return remote_odb.fs, remote_info
if out.is_dir_checksum:
checksum = self._get_granular_hash(path, out).value
cache_path = out.odb.hash_to_path_info(checksum).url
cache_dir = config.get(cls.PARAM_CACHE_DIR)
cache_config = (
{}
if not cache_dir
else {
"cache": {
"dir": cache_dir,
"type": config.get(cls.PARAM_CACHE_TYPES),
}
}
)
repo_kwargs: dict = {
"rev": config.get(cls.PARAM_REV),
"subrepos": config.get(cls.PARAM_SUBREPOS, False),
"uninitialized": True,
}
factory: Optional["RepoFactory"] = None
if url is None:
repo_kwargs["config"] = cache_config
else:
cache_path = out.cache_path
return out.odb.fs, cache_path
repo_kwargs["cache_dir"] = cache_dir
factory = erepo_factory(url, root, cache_config)
def open( # type: ignore
self, path: PathInfo, mode="r", encoding=None, **kwargs
): # pylint: disable=arguments-renamed
fs, fspath = self._get_fs_path(path, **kwargs)
return fs.open(fspath, mode=mode, encoding=encoding)
with _open(
url if url else root,
**repo_kwargs,
) as repo:
return repo, factory
def exists(self, path): # pylint: disable=arguments-renamed
try:
self.metadata(path)
return True
except FileNotFoundError:
return False
def _get_repo(self, path: str) -> "Repo":
"""Returns repo that the path falls in, using prefix.
def isdir(self, path): # pylint: disable=arguments-renamed
try:
meta = self.metadata(path)
return meta.isdir
except FileNotFoundError:
return False
If the path is already tracked/collected, it just returns the repo.
def check_isdir(self, path_info, outs):
if len(outs) != 1:
return True
Otherwise, it collects the repos that might be in the path's parents
and then returns the appropriate one.
"""
if not self.repo.fs.path.isin_or_eq(path, self.repo.root_dir):
# outside of repo
return self.repo
out = outs[0]
if not out.is_dir_checksum:
return out.path_info != path_info
if out.path_info == path_info:
return True
key = self._get_key(path)
repo = self._subrepos_trie.get(key)
if repo:
return repo
try:
self._get_granular_hash(path_info, out)
return False
except FileNotFoundError:
return True
def isfile(self, path): # pylint: disable=arguments-renamed
try:
meta = self.metadata(path)
return meta.isfile
except FileNotFoundError:
return False
def _fetch_dir(self, out, **kwargs):
# pull dir cache if needed
out.get_dir_cache(**kwargs)
if not out.obj:
raise FileNotFoundError
def _add_dir(self, trie, out, **kwargs):
self._fetch_dir(out, **kwargs)
base = out.path_info.parts
for key, _ in out.obj: # noqa: B301
trie[base + key] = None
def _walk(self, root, trie, topdown=True, **kwargs):
dirs = set()
files = []
out = trie.get(root.parts)
if out and out.is_dir_checksum:
self._add_dir(trie, out, **kwargs)
root_len = len(root.parts)
for key, out in trie.iteritems(prefix=root.parts): # noqa: B301
if key == root.parts:
continue
name = key[root_len]
if len(key) > root_len + 1 or (out and out.is_dir_checksum):
dirs.add(name)
continue
files.append(name)
assert topdown
dirs = list(dirs)
yield root.fspath, dirs, files
for dname in dirs:
yield from self._walk(root / dname, trie)
def walk(self, top, topdown=True, onerror=None, **kwargs):
from pygtrie import Trie
assert topdown
root = PathInfo(os.path.abspath(top))
try:
meta = self.metadata(root)
except FileNotFoundError:
if onerror is not None:
onerror(FileNotFoundError(top))
return
if not meta.isdir:
if onerror is not None:
onerror(NotADirectoryError(top))
return
trie = Trie()
for out in meta.outs:
trie[out.path_info.parts] = out
if out.is_dir_checksum and root.isin_or_eq(out.path_info):
self._add_dir(trie, out, **kwargs)
yield from self._walk(root, trie, topdown=topdown, **kwargs)
def walk_files(self, path_info, **kwargs):
for root, _, files in self.walk(path_info):
for fname in files:
# NOTE: os.path.join is ~5.5 times slower
yield PathInfo(f"{root}{os.sep}{fname}")
def isdvc(self, path, recursive=False, strict=True):
try:
meta = self.metadata(path)
except FileNotFoundError:
return False
recurse = recursive or not strict
return meta.output_exists if recurse else meta.is_output
def isexec(self, path_info): # pylint: disable=unused-argument
return False
def metadata(self, path_info):
path_info = PathInfo(os.path.abspath(path_info))
try:
outs = self._find_outs(path_info, strict=False, recursive=True)
except OutputNotFoundError as exc:
raise FileNotFoundError from exc
meta = Metadata(path_info=path_info, outs=outs, repo=self.repo)
meta.isdir = meta.isdir or self.check_isdir(meta.path_info, meta.outs)
return meta
def info(self, path_info):
meta = self.metadata(path_info)
ret = {"type": "directory" if meta.isdir else "file"}
if meta.is_output and len(meta.outs) == 1 and meta.outs[0].hash_info:
hash_info = meta.outs[0].hash_info
ret["size"] = hash_info.size
ret[hash_info.name] = hash_info.value
elif meta.part_of_output:
(out,) = meta.outs
key = path_info.relative_to(out.path_info).parts
obj = out.obj.trie.get(key)
if obj:
ret["size"] = obj.size
ret[obj.hash_info.name] = obj.hash_info.value
return ret
def get_file(
self, from_info, to_file, callback=DEFAULT_CALLBACK, **kwargs
):
fs, path = self._get_fs_path(from_info)
fs.get_file( # pylint: disable=protected-access
path, to_file, callback=callback, **kwargs
prefix_key, repo = self._subrepos_trie.longest_prefix(key)
prefix = self.repo.fs.path.join(
self.repo.root_dir,
*prefix_key, # pylint: disable=not-an-iterable
)
def checksum(self, path_info):
info = self.info(path_info)
md5 = info.get("md5")
if md5:
return md5
raise NotImplementedError
parents = (parent for parent in self.repo.fs.path.parents(path))
dirs = [path] + list(takewhile(lambda p: p != prefix, parents))
dirs.reverse()
self._update(dirs, starting_repo=repo)
return self._subrepos_trie.get(key) or self.repo
@wrap_with(threading.Lock())
def _update(self, dirs, starting_repo):
"""Checks for subrepo in directories and updates them."""
repo = starting_repo
for d in dirs:
key = self._get_key(d)
if self._is_dvc_repo(d):
repo = self.repo_factory(
d,
fs=self.repo.fs,
scm=self.repo.scm,
repo_factory=self.repo_factory,
)
self._datafss[key] = DataFileSystem(repo=repo)
self._subrepos_trie[key] = repo
def _is_dvc_repo(self, dir_path):
"""Check if the directory is a dvc repo."""
if not self._traverse_subrepos:
return False
from dvc.repo import Repo
repo_path = self.repo.fs.path.join(dir_path, Repo.DVC_DIR)
return self.repo.fs.isdir(repo_path)
def _get_fs_pair(
self, path
) -> Tuple[
Optional[FileSystem],
Optional[str],
Optional[DataFileSystem],
Optional[str],
]:
"""
Returns a pair of fss based on repo the path falls in, using prefix.
"""
parts = self.path.relparts(path, self.root_marker)
if parts and parts[0] == os.curdir:
parts = parts[1:]
fs_path = self.repo.fs.path.join(self.repo.root_dir, *parts)
repo = self._get_repo(fs_path)
fs = repo.fs
repo_parts = fs.path.relparts(repo.root_dir, self.repo.root_dir)
if repo_parts[0] == os.curdir:
repo_parts = repo_parts[1:]
dvc_parts = parts[len(repo_parts) :]
if dvc_parts and dvc_parts[0] == os.curdir:
dvc_parts = dvc_parts[1:]
key = self._get_key(repo.root_dir)
dvc_fs = self._datafss.get(key)
if dvc_fs:
dvc_path = dvc_fs.path.join(*dvc_parts) if dvc_parts else ""
else:
dvc_path = None
return fs, fs_path, dvc_fs, dvc_path
def open(
self, path, mode="r", encoding="utf-8", **kwargs
): # pylint: disable=arguments-renamed, arguments-differ
if "b" in mode:
encoding = None
fs, fs_path, dvc_fs, dvc_path = self._get_fs_pair(path)
try:
return fs.open(fs_path, mode=mode, encoding=encoding)
except FileNotFoundError:
if not dvc_fs:
raise
return dvc_fs.open(dvc_path, mode=mode, encoding=encoding, **kwargs)
def isdvc(self, path, **kwargs):
_, _, dvc_fs, dvc_path = self._get_fs_pair(path)
return dvc_fs is not None and dvc_fs.isdvc(dvc_path, **kwargs)
def ls( # pylint: disable=arguments-differ
self, path, detail=True, dvc_only=False, **kwargs
):
fs, fs_path, dvc_fs, dvc_path = self._get_fs_pair(path)
repo = dvc_fs.repo if dvc_fs else self.repo
dvcignore = repo.dvcignore
ignore_subrepos = kwargs.get("ignore_subrepos", True)
names = set()
if dvc_fs:
with suppress(FileNotFoundError):
for entry in dvc_fs.ls(dvc_path, detail=False):
names.add(dvc_fs.path.name(entry))
if not dvc_only and fs:
try:
for entry in dvcignore.ls(
fs, fs_path, detail=False, ignore_subrepos=ignore_subrepos
):
names.add(fs.path.name(entry))
except (FileNotFoundError, NotADirectoryError):
pass
dvcfiles = kwargs.get("dvcfiles", False)
def _func(fname):
from dvc.dvcfile import is_valid_filename
from dvc.ignore import DvcIgnore
if dvcfiles:
return True
return not (
is_valid_filename(fname) or fname == DvcIgnore.DVCIGNORE_FILE
)
names = filter(_func, names)
infos = []
paths = []
for name in names:
entry_path = self.path.join(path, name)
try:
info = self.info(entry_path, ignore_subrepos=ignore_subrepos)
except FileNotFoundError:
continue
infos.append(info)
paths.append(entry_path)
if not detail:
return paths
return infos
def get_file( # pylint: disable=arguments-differ
self, rpath, lpath, callback=DEFAULT_CALLBACK, **kwargs
):
fs, fs_path, dvc_fs, dvc_path = self._get_fs_pair(rpath)
if fs:
try:
fs.get_file(fs_path, lpath, callback=callback, **kwargs)
return
except FileNotFoundError:
if not dvc_fs:
raise
dvc_fs.get_file(dvc_path, lpath, callback=callback, **kwargs)
def info(self, path, **kwargs):
fs, fs_path, dvc_fs, dvc_path = self._get_fs_pair(path)
repo = dvc_fs.repo if dvc_fs else self.repo
dvcignore = repo.dvcignore
ignore_subrepos = kwargs.get("ignore_subrepos", True)
dvc_info = None
if dvc_fs:
try:
dvc_info = dvc_fs.info(dvc_path)
except FileNotFoundError:
pass
fs_info = None
if fs:
try:
fs_info = fs.info(fs_path)
if dvcignore.is_ignored(
fs, fs_path, ignore_subrepos=ignore_subrepos
):
fs_info = None
except (FileNotFoundError, NotADirectoryError):
if not dvc_info:
raise
# NOTE: if some parent in fs_path turns out to be a file, it means
# that the whole repofs branch doesn't exist.
if fs and not fs_info and dvc_info:
for parent in fs.path.parents(fs_path):
try:
if fs.info(parent)["type"] != "directory":
dvc_info = None
break
except FileNotFoundError:
continue
if not dvc_info and not fs_info:
raise FileNotFoundError
info = _merge_info(repo, fs_info, dvc_info)
info["name"] = path
return info
def checksum(self, path):
fs, fs_path, dvc_fs, dvc_path = self._get_fs_pair(path)
try:
return fs.checksum(fs_path)
except FileNotFoundError:
return dvc_fs.checksum(dvc_path)
class DvcFileSystem(FileSystem):
protocol = "local"
PARAM_CHECKSUM = "md5"
def _prepare_credentials(self, **config):
return config
@wrap_prop(threading.Lock())
@cached_property
def fs(self):
return _DvcFileSystem(**self.fs_args)
def isdvc(self, path, **kwargs):
return self.fs.isdvc(path, **kwargs)
@property
def path(self): # pylint: disable=invalid-overridden-method
return self.fs.path
@property
def repo(self):
return self.fs.repo
@property
def repo_url(self):
return self.fs.repo_url
@property
def config(self):
return self.fs.config
def from_os_path(self, path):
if os.path.isabs(path):
path = os.path.relpath(path, self.repo.root_dir)
return as_posix(path)

Some files were not shown because too many files have changed in this diff Show More