Compare commits

...
207 Commits
Author SHA1 Message Date
Ruslan Kuprieiev 4d51f78074 dvc: bump to 0.68.0 2019-11-11 01:53:58 +02:00
Ruslan Kuprieiev 204aa9c6c3 Merge pull request #2773 from n3hrox/build_patch
Add better heuristic for build detection
2019-11-11 01:33:06 +02:00
n3hrox 53f4ad2943 add build cleanup 2019-11-10 23:56:28 +01:00
n3hrox 448ae95604 fix windows echo 2019-11-10 22:22:25 +01:00
n3hrox 3ba4b8bd4d remove unnecessary pip check 2019-11-10 22:10:19 +01:00
n3hrox 172ad71553 fix typo 2019-11-10 21:46:36 +01:00
n3hrox 1156763e84 add pip fallback 2019-11-10 21:40:46 +01:00
n3hrox a0efedcd31 remove arch support from readme 2019-11-10 21:02:07 +01:00
n3hrox 707cce9bd2 remove unnecessary line 2019-11-10 20:24:26 +01:00
n3hrox c68adc9a6d remove windows patch in favor of echo 2019-11-10 20:23:14 +01:00
n3hrox 4fdf269450 fix dvc build path 2019-11-10 20:06:19 +01:00
n3hrox c48c84261d remove patches in favor of echo 2019-11-10 20:03:32 +01:00
n3hrox 36f75dd519 Remove unnecessary file 2019-11-10 19:48:15 +01:00
n3hrox e1dfb75055 Refactor get_package_manager 2019-11-10 19:46:48 +01:00
n3hrox f4ee4b0434 Add better heuristic for build detection 2019-11-10 18:04:41 +01:00
Ruslan Kuprieiev 387a471857 dvc: bump to 0.67.1 2019-11-10 18:14:09 +02:00
Ruslan Kuprieiev f246ec1187 pyinstaller: add win32timezone hidden import
Fixes #2772
2019-11-10 18:13:31 +02:00
Ruslan Kuprieiev cfc442e932 Merge pull request #2769 from algomaster99/test-arg-types-move
Ensure `move` accepts str and Path-like objects
2019-11-09 23:27:31 +02:00
Aman Sharma 9427bbe8d0 Move move to filesystem utility 2019-11-09 23:57:57 +05:30
Ruslan Kuprieiev 1fd4ab2655 dvc: bump to 0.67.0 2019-11-09 19:40:55 +02:00
Ruslan Kuprieiev 663eaa0c36 Merge pull request #2767 from casperdcl/locking
non-blocking intermediate progress IO
2019-11-09 19:07:31 +02:00
Aman Sharma 44268251b4 Ensure move accepts str and Path-like objects 2019-11-09 18:47:01 +05:30
Casper da Costa-Luis 2e1360a34f non-blocking intermediate progress IO
- fixes #2766
2019-11-09 01:43:47 +00:00
Ruslan Kuprieiev 143cbdc828 dvc: bump to 0.66.11 2019-11-09 02:08:50 +02:00
Ruslan Kuprieiev 49e437501d Merge pull request #2764 from efiop/2754
win: use SuppressibleMsgBox instead of MsgBox
2019-11-09 02:08:02 +02:00
Ruslan Kuprieiev add0958019 win: inno: use SuppressibleMsgBox instead of MsgBox 2019-11-09 01:47:49 +02:00
Ruslan Kuprieiev 0d057ef24b Merge pull request #2763 from efiop/2754
pyinstaller: add pywin32/win32file hook
2019-11-09 00:24:28 +02:00
Ruslan Kuprieiev a956090d6d pyinstaller: add pywin32/win32file hook
Also added a simple test to `dvc-test` repo [1].

[1] https://github.com/iterative/dvc-test/commit/5748744fa1ebb4e265f08fedcddc66a17e8bd914

Fixes #2754
2019-11-08 22:11:17 +02:00
Ruslan Kuprieiev a56a6a1bd2 dvc: bump to 0.66.10 2019-11-08 21:04:22 +02:00
Ruslan Kuprieiev 183b699dee Merge pull request #2761 from iterative/efiop-patch-1
dvc: add workaround for `LookupError: unknown encoding: idna`
2019-11-08 20:56:08 +02:00
Ruslan Kuprieiev 1411f08121 Merge pull request #2762 from iterative/efiop-patch-1-restyled
Restyle dvc: add workaround for `LookupError: unknown encoding: idna`
2019-11-08 20:26:26 +02:00
Restyled.io 43cc308078 Restyled by black 2019-11-08 18:22:27 +00:00
Ruslan Kuprieiev a0b6c38fa5 dvc: add workaround for LookupError: unknown encoding: idna
Fixes #2749
2019-11-08 20:20:22 +02:00
Ruslan Kuprieiev 54760c5604 Merge pull request #2758 from kss682/test_2672
Test for issue #2672 added.
2019-11-08 19:42:24 +02:00
kss682 7e336a8e7a test for issue 2672 --typo correction 2019-11-09 21:12:24 +05:30
kss682 4409c60468 Test for issue #2672 added. 2019-11-09 20:41:40 +05:30
Ruslan Kuprieiev 5ad7d9802e Merge pull request #2757 from algomaster99/test-arg-types-makedirs
Ensure `makedirs` accepts str and Path-like objects
2019-11-08 17:56:40 +02:00
Aman Sharma 204cfcb879 Ensure makedirs accepts str and Path-like objects 2019-11-08 20:37:32 +05:30
Ruslan Kuprieiev ec44a614b0 Merge pull request #2753 from efiop/analytics
analytics: don't verify config from repo
2019-11-08 14:38:02 +02:00
Ruslan Kuprieiev 1058df0f77 analytics: don't forget to convert to bool 2019-11-08 05:21:47 +02:00
Ruslan Kuprieiev 82be22a2ad Merge pull request #2752 from algomaster99/test-arg-types-copyfile
Ensure `copyfile` accepts str and Path-like objects
2019-11-08 02:54:37 +02:00
Ruslan Kuprieiev 910a67b35f analytics: don't verify config from repo
Otherwise this will raise an exception from analytics worker:
```
dvc config cache.type sometypo
```
But running that same command with `--system` or `--global` flag outside
of repo won't raise that.

Also need to better handle ConfigObjError, but that is a separate issue.
2019-11-08 02:37:11 +02:00
Aman Sharma d4aa27d6ab Ensure copyfile accepts str and Path-like objects 2019-11-08 01:51:49 +05:30
Ruslan Kuprieiev 7387760755 Merge pull request #2751 from pared/2742
ignore: make DvcIgnoreFilter use tree
2019-11-07 19:45:04 +02:00
Ruslan Kuprieiev c4040401c5 Merge pull request #2750 from efiop/2671
checkout: don't cleanup links when checking out specific targets
2019-11-07 19:40:35 +02:00
pawel 4acfaa293e ignore: add test for dvcignore on branch 2019-11-07 17:51:00 +01:00
pawel f14aca630f ignore: make DvcIgnoreFilter use tree 2019-11-07 15:37:24 +01:00
Ruslan Kuprieiev 1af608bf64 checkout: don't cleanup links when checking out specific targets
Related to #2671
2019-11-07 13:10:16 +02:00
Ruslan Kuprieiev c60b82d31d dvc: bump to 0.66.9 2019-11-07 00:25:28 +02:00
Ruslan Kuprieiev 471add516e Merge pull request #2748 from efiop/logger
system: use logging instead of dvc.logger
2019-11-07 00:24:32 +02:00
Ruslan Kuprieiev 21b7d1f7ec system: use logging instead of dvc.logger
A leftover from an old logger refactoring.
2019-11-06 23:24:46 +02:00
Ruslan Kuprieiev 0a9e6944b0 updater: fix typo 2019-11-06 16:03:59 +02:00
Ruslan Kuprieiev fb8315997e Merge pull request #2746 from algomaster99/test-arg-types-file_md5
Ensure `file_md5` accept str and Path-like objects
2019-11-06 14:01:20 +02:00
Aman Sharma 34a868b420 Ensure file_md5 accept str and Path-like objects 2019-11-06 09:25:35 +05:30
Ruslan Kuprieiev b6102f8d30 dvc: bump to 0.66.8 2019-11-06 03:09:04 +02:00
Ruslan Kuprieiev ab0ba77ab2 Merge pull request #2745 from shcheklein/fix-2744
dvc get leaves dangling symlinks if source repo is set to use symlinks as its cache type
2019-11-06 03:08:37 +02:00
Ivan Shcheklein ffd8c2cf6e tests: add dvc import test to check that local cache type is enforced 2019-11-05 16:40:24 -08:00
Ivan Shcheklein c25715dc72 get: fix override external repo cache type 2019-11-05 16:27:01 -08:00
Ivan Shcheklein 73f95ee6b4 tests: minor style and typo fixes in dvc gc tests 2019-11-05 16:25:38 -08:00
Ruslan Kuprieiev 418b0b6523 dvc: bump to 0.66.7 2019-11-06 02:13:10 +02:00
Ruslan Kuprieiev e30d3545cd Merge pull request #2743 from efiop/2737
dvcignore: don't forget to use dvcignore when collecting dvcignores
2019-11-06 02:11:11 +02:00
Ruslan Kuprieiev 7caf28ca11 dvcignore: don't forget to use dvcignore when collecting dvcignores
Fixes #2737
2019-11-06 01:39:31 +02:00
Ruslan Kuprieiev 5e2fa3742b Merge pull request #2741 from algomaster99/remove-get-parent-dirs
Remove `get_parent_dirs_up_to`
2019-11-05 23:01:31 +02:00
Ruslan Kuprieiev 9145dcd6a8 Merge pull request #2740 from algomaster99/test-arg-types-symlink
Ensure `contains_symlink_up_to` accept both str and Path-like objects
2019-11-05 22:42:06 +02:00
Aman Sharma 8491ddf43f Remove get_parent_dirs_up_to 2019-11-06 02:03:47 +05:30
Aman Sharma f642ff76a5 Ensure contains_symlink_up_to accept both str and Path-like objects 2019-11-06 00:24:10 +05:30
Ruslan Kuprieiev a4c57bbb55 Merge pull request #2739 from mroutis/sftp-move-fix
sftp: only remove src after an atomic copy during move
2019-11-05 19:01:35 +02:00
Mr. Outis 0103f3330f sftp: only remove src after an atomic copy during move
Shout-out to @Suor for pointing this out
2019-11-05 10:02:41 -06:00
Ruslan Kuprieiev 62c5e12d43 dvc: bump to 0.66.6 2019-11-05 03:20:24 +02:00
Ruslan Kuprieiev ee70822bd4 Merge pull request #2736 from efiop/master
dvc: automatically reorder imports
2019-11-05 03:02:32 +02:00
Ruslan Kuprieiev 95fe1578bb dvc: automatically reorder imports 2019-11-05 02:34:40 +02:00
Ruslan Kuprieiev ba8360d4fd Merge pull request #2735 from efiop/win
system: win: use win32file for _getdirinfo
2019-11-05 02:19:23 +02:00
Ruslan Kuprieiev b718b839bf system: win: use win32file for _getdirinfo
Current implementation is triggering some non-obvious gc bug, that
causes memory leaks. Using win32file simplifies the code and gets rid of
this bug.

To reproduce it, run:

```
from dvc.system import System

while True:
    System.inode(__file__)
```

and see your Windows machine run out of memory pretty quickly.
2019-11-05 01:46:21 +02:00
Ivan Shcheklein 9af4b964eb update Github PR template: completely get rid of newlines to make formatting sane 2019-11-04 15:43:05 -08:00
Ivan Shcheklein bc543ac4b0 update PR Github template, Github does not respect soft line breaks in certain cases
More info: https://github.com/isaacs/github/issues/1013
2019-11-04 15:39:24 -08:00
Ruslan Kuprieiev 197a802c61 Merge pull request #2734 from shcheklein/fix-get-npe
fix NPE when we do get that does not provide progress callback
2019-11-05 00:17:41 +02:00
Ivan Shcheklein 8f403c0704 fix NPE when we do get that does not provide progress callback 2019-11-04 13:01:28 -08:00
Ruslan Kuprieiev 522579be1e dvc: bump to 0.66.5 2019-11-04 22:55:08 +02:00
Ruslan Kuprieiev a5d317eaa2 Merge pull request #2679 from n3hrox/enhance/conda_detection
updater: detect conda
2019-11-04 22:54:05 +02:00
n3hrox 83429a5abc refactor is_conda placement 2019-11-04 21:29:56 +01:00
n3hrox b97865e165 fix test 2019-11-04 21:18:07 +01:00
n3hrox 8e2d0bdcd6 Merge branch 'master' of github.com:iterative/dvc into enhance/conda_detection 2019-11-04 21:10:57 +01:00
n3hrox e3417f78be revert changes 2019-11-04 21:02:51 +01:00
n3hrox 4da42d188c use patch heuristic to determine conda 2019-11-04 21:00:09 +01:00
n3hrox 952fee57c1 resolve conflicts 2019-11-04 20:52:24 +01:00
Ruslan Kuprieiev 35507dfc61 Update .restyled.yaml 2019-11-04 20:31:20 +02:00
Ruslan Kuprieiev 8b4cc01110 add restyled.io 2019-11-04 20:20:22 +02:00
Ruslan Kuprieiev 3974862f87 github: add config for release-drafter bot 2019-11-04 19:13:39 +02:00
Ruslan Kuprieiev e0fe8a8f94 Merge pull request #2723 from shcheklein/fix-pr-template
fix PR template to mention recommendatory nature of CC and DS
2019-11-04 11:36:03 +02:00
Ivan Shcheklein cbb58e02d6 PR template: change wording for the docs checkbox 2019-11-03 17:02:11 -08:00
Ivan Shcheklein 8b05eb5d36 PR template: minor sentence with emoji style change 2019-11-03 16:19:05 -08:00
Ivan Shcheklein 60bdd41cbd PR template: address review, update DS and CC checks item 2019-11-03 16:17:23 -08:00
Ivan Shcheklein d0e62a57ab fix PR template to mention recommendatory nature of CC and DS 2019-11-03 15:15:23 -08:00
Ruslan Kuprieiev 5227fc58d5 dvc: bump to 0.66.4 2019-11-04 00:38:10 +02:00
Ruslan Kuprieiev 4efd048796 test: try removing get_url test 2019-11-04 00:05:44 +02:00
Ruslan Kuprieiev 46391cb4f9 test: fix formatting 2019-11-03 23:47:34 +02:00
Ruslan Kuprieiev 71f8d25ab3 test: get-url: try using with mock_s3() instead of a decorator 2019-11-03 23:42:44 +02:00
Ruslan Kuprieiev 2df7eda159 Merge pull request #2674 from n3hrox/enhance/footnote_deduplication
logger: output footer once for multithreaded push/pull errors
2019-11-03 23:37:13 +02:00
n3hrox d25282ba28 revert print to logger 2019-11-03 22:14:59 +01:00
n3hrox 2893c9acad move footer to better place and change logging to stderr 2019-11-03 22:07:27 +01:00
Ruslan Kuprieiev 46d221fade test: s3: try using boto3.client 2019-11-03 22:38:48 +02:00
Ruslan Kuprieiev 17512feb52 Merge pull request #2722 from kss682/2672_1
Added package manager to dvc version output
2019-11-03 21:56:01 +02:00
kss682 3421c6c6e4 Added get_package_manager from dvc.utils.pkg.
Solves issue #2672
2019-11-05 00:58:27 +05:30
kss682 8903dbbc03 Deleted duplicate from updater
Solves issue #2672
2019-11-05 00:31:55 +05:30
kss682 0786a70697 Package added with get_package_manager function and other utils.
import changed accordingly in version
Solves issue #2672
2019-11-05 00:15:56 +05:30
kss682 bb7a9a567e Added package manager to dvc version output.
Solves issue #2672
2019-11-04 20:29:39 +05:30
Ruslan Kuprieiev e75c52a170 setup: freeze awscli version 2019-11-03 15:09:29 +02:00
Ruslan Kuprieiev 3514cc5b5a test: freeze moto version 2019-11-03 14:49:36 +02:00
Ruslan Kuprieiev 0fece74d00 test: get-url: don't use get_aws_url 2019-11-03 14:18:25 +02:00
Ruslan Kuprieiev 1b54c7fa6a Merge pull request #2715 from ATR-oCiTy/master
Fixed issue #2680 and Simple typo rectified
2019-11-03 12:51:43 +02:00
Ruslan Kuprieiev 6400eba83f Merge pull request #2716 from eshaan-jeckyll/master
typos corrected
2019-11-03 12:48:55 +02:00
Ruslan Kuprieiev 6559bae0c9 Merge pull request #2717 from johntharian/master
typo changed for existance->existence
2019-11-03 12:48:50 +02:00
Ruslan Kuprieiev a65e5c37fb Merge pull request #2714 from Pandemic179/master
Rectified simple typos
2019-11-03 12:44:56 +02:00
Ruslan Kuprieiev c71e5562bb Merge pull request #2713 from GeorgeSabu-mec/master
fix typo(correct spelling of existence)
2019-11-03 12:44:03 +02:00
Ruslan Kuprieiev 601a77ed4b Merge pull request #2712 from the-thirteenth-star/master
fixed typo
2019-11-03 12:42:37 +02:00
johntharian 55d819494a typo changed for existance->existaence 2019-11-03 15:51:37 +05:30
GeorgeSabu-MEC 1e22fabece fix typo(correct spelling of existence) 2019-11-03 15:42:07 +05:30
Naina 1afc40c1ec Update fetch.py 2019-11-03 15:40:27 +05:30
ATR-oCiTy 2c0b209e38 Rectified install: Cask 'dvc' is not installed. error #2680 2019-11-03 15:25:30 +05:30
ATR-oCiTy e26517ec36 Simple typos mectified 2019-11-03 15:04:42 +05:30
eshaan-jeckyll 97b4800d2e typos corrected 2019-11-03 15:03:36 +05:30
Pandemic179 4007da0298 Rectified simple typos 2019-11-03 15:02:30 +05:30
Ruslan Kuprieiev 372e1070ac Merge pull request #2703 from kurianbenoy/master
Remove multi-threaded prefix before progress bar(#2686)
2019-11-03 00:34:19 +02:00
Ruslan Kuprieiev e21fc34c84 Merge pull request #2698 from algomaster99/test-argument-types-get_mtime_and_size
Verify if `get_mtime_and_size` can take both Path objects and strings
2019-11-03 00:25:47 +02:00
Aman Sharma cffc9c8ed9 Remove unnecessary wrapping of path 2019-11-03 02:38:50 +05:30
Aman Sharma d454cb8637 Modify test to check for directory as well 2019-11-03 02:23:46 +05:30
Casper da Costa-Luis 440b323e39 TqdmThreadPoolExecutor => ThreadPoolExecutor
- Fixes https://github.com/iterative/dvc/pull/2703#discussion_r341763150
2019-11-02 15:22:46 +00:00
Casper da Costa-Luis 82dae6c25b remove special TqdmThreadPoolExecutor logic
- fixes #2686
- closes #2703
2019-11-02 15:22:46 +00:00
kurianbenoy 306c916626 Change import order in progress file 2019-11-02 15:22:46 +00:00
kurianbenoy 21bafccc96 Remove multi-threaded prefix before progress bar(#2686) 2019-11-02 15:22:46 +00:00
Ruslan Kuprieiev 2e6485f24b Merge pull request #2709 from mroutis/fix-2704
sftp: make move across drives possible
2019-11-02 14:07:46 +02:00
Ruslan Kuprieiev 91a7af03e7 Merge pull request #2707 from Suor/urlinfo
perf: refactor URLInfo to not use ParseResult inside
2019-11-02 13:49:49 +02:00
Mr. Outis 16164e65ad sftp: move -> use atomic copying 2019-11-01 17:01:50 -06:00
Ruslan Kuprieiev 809a85a340 dvc: bump to 0.66.3 2019-11-01 23:58:17 +02:00
Ruslan Kuprieiev ce8e019dd1 Merge pull request #2705 from shcheklein/fix-2702
get-url: fix NPE when src non local and dst not absolute
2019-11-01 23:54:53 +02:00
Ruslan Kuprieiev 7ecbdab564 Merge pull request #2693 from sadielbartholomew/i2685-pull-cmd-output
pull, at import stage: replace warning with info report
2019-11-01 23:15:23 +02:00
Aman Sharma f4f9f3aac1 Replace path with path_info 2019-11-02 01:15:17 +05:30
Mr. Outis 8f7000056b sftp: move -> fallback to copy when sftp.rename fails 2019-11-01 13:26:03 -06:00
Mr. Outis 283f848822 sftp: make move an atomic operation 2019-11-01 13:17:22 -06:00
Mr. Outis ab1ead881b sftp: make move across drives possible
Fix #2704
2019-11-01 12:40:16 -06:00
n3hrox 89502bee88 fix tests 2019-11-01 18:05:27 +01:00
Ivan Shcheklein 3660d9eac4 local output: address PR comment, fix style a bit 2019-11-01 09:49:56 -07:00
Alexander Schepanovski b83564d80e perf: refactor URLInfo to not use ParseResult inside
This permites URLInfo construction from parts without building/reparsing
string, which makes it around 20% faster. Code is also simpler and a bit
shorter now.

Other improvements:
- added URLInfo.replace(path=...)
- URLInfo._path now stringifies properly
- protected against passing path not starting with /
- do not allow query, params, fragments and passwords in urls (they were
silently ignored previously)
2019-11-01 17:33:00 +01:00
n3hrox 8ae1165a9f fix typo upload -> download 2019-11-01 16:38:23 +01:00
n3hrox 3bf810a832 move footer from logger to main 2019-11-01 16:36:31 +01:00
Ruslan Kuprieiev c999b5d270 remove info message 2019-11-01 16:45:41 +02:00
Ruslan Kuprieiev 5c8a312002 fix order 2019-11-01 16:41:22 +02:00
Ruslan Kuprieiev 0dd4d2361e fix formatting 2019-11-01 16:40:06 +02:00
Sadie Bartholomew 31551e6e2e CLI: change return output for 'checkout' and 'pull' commands
Change messages output after return of some commands:

* Remove warning for 'dvc checkout' & commands calling it under the hood
* Add an informational message output for 'dvc pull' command

Fixes #2685.
2019-10-31 23:01:24 +00:00
Ivan Shcheklein 98750c95c5 get-url: fix NPE when src non local and dst not absolute 2019-10-31 09:58:07 -07:00
Ruslan Kuprieiev d932405ee1 Merge pull request #2695 from mroutis/test-s3-etag
tests: preserve the same ETag across buckets during copy
2019-10-31 01:55:17 +02:00
Aman Sharma b9a17752e3 Test acceptable path types of get_mtime_and_size 2019-10-31 00:30:54 +05:30
Mr. Outis 732ed904ce tests: same ETag across buckets 2019-10-30 11:42:39 -06:00
Ruslan Kuprieiev e623c4faf7 dvc: bump to 0.66.2 2019-10-30 17:20:05 +02:00
Ivan ShchekleinandRuslan Kuprieiev 2ec0aa0c37 failing while pulling imported directories (#2692)
* pull: fix-2684 - failing while pulling imported directories

* pull: fix-2684 - fix code indentation

* minor imported directories pull test update

Co-Authored-By: Ruslan Kuprieiev <[email protected]>
2019-10-30 17:18:07 +02:00
Alexander Schepanovski 77fcd49695 dvc: fix tqdm dep (#2694)
tqdm==4.35.0 was throwing KeyError: 'percentage'.
2019-10-30 16:43:18 +02:00
Ruslan Kuprieiev 877e38551e lock: add a workaround for a flufl.lock bug (#2688)
This bug doesn't affect us during regular dvc CLI commands, but is not
visualy pleasing during API usage.

Fixes #2573
2019-10-29 20:54:56 +02:00
Jorge Orpinel a48481b01f dvc: change ` for ' in dynamic parts of output messages (#2644)
* dvc: change ` for ' in dinamic parts of DVC outputs

per https://github.com/iterative/dvc/pull/2644#issuecomment-545151398

* dvc: use ' for dynamic output and ` for DVC names

per https://github.com/iterative/dvc/pull/2644#discussion_r338417698
2019-10-28 22:00:53 +02:00
Sagun Karanjit 5d6918a04c Remove --all-commits options from usage output (#2682)
* Comment out --all-commits optional arg

* Suppress --all-commits optional arg
2019-10-28 17:27:34 +02:00
Aman Sharma 5f644745c7 Write test for get_inode (#2673) 2019-10-28 16:43:47 +02:00
n3hrox 82b69a5abd remove cask check 2019-10-27 22:31:25 +01:00
n3hrox 752e584a9b refactor function and add test 2019-10-27 22:25:27 +01:00
n3hrox 03a2f284e2 refactor 2019-10-27 18:28:38 +01:00
n3hrox 225d538069 reduce amount of returns 2019-10-27 18:19:45 +01:00
n3hrox 795db730fe switch subprocess to popen 2019-10-27 18:15:16 +01:00
n3hrox cf9037932d change method to static 2019-10-27 18:03:33 +01:00
n3hrox a9b9d52853 updater: detect conda 2019-10-27 17:59:04 +01:00
n3hrox 98bafa9968 Change f-string to old format 2019-10-26 23:01:34 +02:00
Ruslan Kuprieiev b9c7d88fcc dvc: bump to 0.66.1 2019-10-26 23:36:13 +03:00
Ruslan Kuprieiev 64f78c164d Revert "Added path.lstrip for / in imports (#2651)" (#2675)
This reverts commit bfc0a3596f.
2019-10-26 23:35:22 +03:00
n3hrox 2e12af095e Fix improper variable name 2019-10-26 22:21:58 +02:00
n3hrox a768fd13ff logger: output footer once for multithreaded push/pull errors 2019-10-26 22:14:14 +02:00
Ruslan Kuprieiev 1baf020de7 s3: don't use walk_files for list_cache_paths
No need to convert back and forth from string to PathInfo
2019-10-26 00:29:23 +03:00
Ruslan Kuprieiev 1ab9319682 gs: fix typo 2019-10-25 23:24:43 +03:00
Ruslan Kuprieiev 32ba773aee s3: don't forget to convert to regular path strings 2019-10-25 23:07:17 +03:00
Ruslan Kuprieiev fc485581f9 dvc: bump to 0.66.0 2019-10-25 22:46:50 +03:00
Ruslan Kuprieiev f3bacb7c28 gs: retry upload with smaller chunk size (#2661)
Default chunk size for gs is 100M, which might be too much for
particular network. So if we are getting ConnectionError, we
should try lowering the chunk size until we reach the minimum
allowed chunk size of 256K. Also note that `chunk_size` must be a
multiple of 256K per the API specification.

Fixes #2572
2019-10-25 22:45:54 +03:00
Sanket Saurav 88f45c2348 Exclude compat files from DeepSource analysis (#2670)
Hi team,

I just [noticed](https://github.com/iterative/dvc/pull/2548#issuecomment-536255251) that there are could be quite a number of false-positives in `dvc/utils/compat.py` because of, well, the things we have to do for compatibility. I would recommend adding that file to `exclude_patterns` to prevent the noise. I've proposed that in this PR.

We've seen some other projects report this as well, and we're working on proactively suggesting this via the product UI to make it easier.

Hope this helps!

Best,
Sanket
Founder, DeepSource
2019-10-25 15:28:09 +03:00
Mr. Outis 27b63e2674 s3: support for directories (#2619)
* s3: list_objects -> list_objects_api

* s3: use path_info for _list_paths

* s3: separate _list_paths from _list_objects

* s3: support max items for listing objects/paths

* s3: take into account directories on `exists`

* s3: overwrite `isdir` method

* s3: custom collect directory entry for s3

* s3: strip checksum when collecting directories

* remote: change walk implementation to walk_files

* s3: add comment about isdir logic

* s3: use `dir_path` correctly on `isdir`

* tests: add a few unit tests for S3

* s3: use path_info join instead of posixpath

* tests: use parametrize for s3 tests

* remote: use posixpath accordingly

* tests: remove undeeded AWS variables from S3 test

* tests: rearrange parametrize values for readability

* tests: add encoding, use a single s3 instance, for loop instead of parameterize

* py2: fix encoding
2019-10-25 03:41:04 +03:00
Ruslan Kuprieiev 35499faa7d readme: remove brew cask and update brew install cmd 2019-10-25 01:41:46 +03:00
Casper da Costa-Luis 9ce3b0c50c add: output improvements (#2546)
* some doc improvements

* add: intermediate progress

* update after rebase

* linting

* fix checksum progress cleanup by ensuring gc

* progress-aware logger.info

* re-fix checksum pbar closure

* multi-file add

* make stage.dump() less verbose.

TODO: check all instances of stage.dump and possibly make verbose

* decrease verbosity of checksum calculations

* leave dvc.add stats

* update/fix logger tests

* minor doc reword

Co-Authored-By: Ruslan Kuprieiev <[email protected]>

* tidy API target_;list => targets

- as per https://github.com/iterative/dvc/pull/2546#discussion_r337042535

* tidy RecursiveAddingWhileUsingFilename message duplication

- fixes https://github.com/iterative/dvc/pull/2546#discussion_r337044670

* minor logging uncomment

- fixes https://github.com/iterative/dvc/pull/2546#discussion_r337051097

* minor doc improvements

- https://github.com/iterative/dvc/pull/2546#discussion_r337116255
- https://github.com/iterative/dvc/pull/2546#discussion_r337116809

* tidy exception

- fixes https://github.com/iterative/dvc/pull/2546#discussion_r337434142

* minor doc update

- fixes https://github.com/iterative/dvc/pull/2546/files#r337294491
- related to https://github.com/iterative/dvc.org/issues/719

* Adding => add

- fixes https://github.com/iterative/dvc/pull/2546#discussion_r337037137

* various review fixes

- minor comment as per
  https://github.com/iterative/dvc/pull/2546#discussion_r337927837
- revert warning style fix
  https://github.com/iterative/dvc/pull/2546#discussion_r338046035
- Abstract error consistency
  https://github.com/iterative/dvc/pull/2546#discussion_r337931070
- minor comment as per
  https://github.com/iterative/dvc/pull/2546#discussion_r338254958

* remove diabling progress option

- fixes https://github.com/iterative/dvc/pull/2546#discussion_r338314050
2019-10-25 01:09:19 +03:00
Ruslan Kuprieiev 3667830c2a dvc: bump to 0.65.0 2019-10-24 23:41:43 +03:00
Ruslan Kuprieiev 8d9e4ca934 graph: use find_cycle instead of simple_cycle (#2668)
We are incorrectly using `simple_cycle` here, `find_cycle` is much more
suitable for this application and much faster too. For example:

```
$ cat test_networkx.py
import networkx as nx
from dvc.repo.graph import check_acyclic

G = nx.scale_free_graph(1200)

list(nx.find_cycle(G, orientation='ignore'))

list(nx.simple_cycles(G))

$ python -mcProfile -scumtime test_networkx.py &> log.log
```

shows ~100s for `simple_cycles` and ~0s for `find_cycle`.

Fixes #2666
2019-10-24 23:41:11 +03:00
Casper da Costa-Luis ed1f643a6e ssh credential prompt before progress (#2660)
* ssh credential prompt before progress

* clean up ssh cred handling

- fixes https://github.com/iterative/dvc/pull/2660#discussion_r338536169
2019-10-24 19:14:07 +03:00
Ruslan Kuprieiev 24ed639901 setup: add upper limit for tqdm version
https://github.com/iterative/dvc/issues/2659#issuecomment-545940739
2019-10-24 17:29:53 +03:00
Ruslan Kuprieiev b8d1ec204c dvc: bump to 0.64.1 2019-10-24 17:15:30 +03:00
Casper da Costa-Luis 6f068857cb Desc fix (#2663)
* fix zero-width desc again

- fixes #2659

* correctly use tqdm API
2019-10-24 17:05:52 +03:00
Ruslan Kuprieiev 8d067c8805 setup: update project url 2019-10-24 15:19:04 +03:00
Dashamir Hoxha 7879c7cb31 Update URLs by replacing /contributing with /contributing/code (#2657)
* Update URLs by replacing /contributing with /contributing/code

* Rename contributing/code to contributing/core
2019-10-24 02:12:39 +03:00
Ruslan Kuprieiev bd5a8a5cef dvc: bump to 0.64.0 2019-10-23 22:48:33 +03:00
Ruslan Kuprieiev 480a612da4 output: base: don't forget to return empty cache (#2656)
* output: base: don't forget to return empty cache

CC @Suor

Fixes https://gist.github.com/leimbernon/21924e02a998e3f1604990f8a7824c37

Signed-off-by: Ruslan Kuprieiev <[email protected]>

* gc: add test for missing .dir cache file
2019-10-23 22:47:50 +03:00
Alexander Schepanovski 86811aac20 imp: dedup and parallelize fetching imported deps (#2654)
* imp: dedup and parallelize external fetches

This prevents same fetches for external artifacts, groups them by source
and fetches in parallel.

* imp: test that fetch works on non workspace deps
2019-10-23 18:02:50 +03:00
Alexander Schepanovski 30341438e0 gc: added --all-commits (#2643)
* dvc: fix branch suffixes in status/push/pull

* gc: add --all-commits flag

* gc: fix gc warning

Say that we always leave things used in the working tree
2019-10-23 18:00:31 +03:00
Srividya Krishnakumar bfc0a3596f Added path.lstrip for / in imports (#2651) 2019-10-23 03:54:10 +03:00
Ruslan Kuprieiev a0d364e78a version: fix warning 2019-10-22 23:21:22 +03:00
Ruslan Kuprieiev 878998433f dvc: bump to 0.63.4 2019-10-22 01:26:02 +03:00
Ruslan Kuprieiev 763bfb4734 http: reuse requests.Session (#2646)
This way we are able to properly utilize automatic connection pools and
not create new fds for each request, which overflows ulimit for max fds
very quickly on mac and windows. Kudos @pared for investigating 🎉

Fixes #2600

Signed-off-by: Ruslan Kuprieiev <[email protected]>
2019-10-22 00:54:29 +03:00
Ruslan Kuprieiev 2274261808 progress: enable progress bar if it is a TTY
Fixes #2645
2019-10-21 23:35:47 +03:00
Ruslan Kuprieiev 3043ed2cb9 lock: skip using slow getaddrinfo (#2642)
This is basically Lock.__init__ copy-paste, except that
instead of using `socket.getfqdn()` we use `socket.gethostname()`
to speed this up. We've seen [1] `getfqdn()` take ~5sec to return
anything, which is way too slow. `gethostname()` is actually a
fallback for `getfqdn()` when it is not able to resolve a
canonical hostname through network. The claimfile that uses
`self._hostname` is still usable, as it uses `pid` and random
number to generate the resulting lock file name, which is unique
enough for our application.

[1] https://github.com/iterative/dvc/issues/2582

Fixes #2582
2019-10-21 19:20:08 +03:00
Ruslan Kuprieiev 59e62a7227 checkout: accept multiple targets (#2640)
Make `Repo.checkout` accept a list of targets, instead of having to feed
it targets one-by-one. This allows us to properly handle checkout
errors.

Fixes #2580
2019-10-21 17:06:21 +03:00
Alexander Schepanovski 33b0eefec0 dvc: dedup used cache (#2630)
* dvc: dedup used cache

This is a preparation step to make garbage collector work with more
commits. It has a value of its own:
- less exists calls to show cloud status or prepare push/pull
- same names are not repeated in status and pbar texts

* dvc: delay checksum names concatenation
2019-10-21 12:12:56 +03:00
Alexander Schepanovski 1f58fc6f42 dvc: dedup brancher (#2639) 2019-10-20 17:32:42 +03:00
Casper da Costa-Luis 1e01ce3ac9 isatty (#2550)
* suppress Tqdm when isatty

- fixes #2549

* fix tests

* unconditionally disable Tqdm if not isatty

- fixes https://github.com/iterative/dvc/pull/2550#discussion_r329386950

* add TQDM_DISABLE env var

- update tests
- add documentation

* TQDM_DISABLE => TQDM_ISATTY

* tests: mock.patch sys.stderr

* TQDM_ISATTY => DVC_ISATTY

as per https://github.com/iterative/dvc/pull/2550#discussion_r334565120

* dvc.progress.Tqdm file kwarg

- fixes https://github.com/iterative/dvc/pull/2550/files/ad1f245107c405697c4b61fe58a92fc0265c6588#r335706955

* dynamic env DVC_ISATTY

- fixes
  https://github.com/iterative/dvc/pull/2550/files/ad1f245107c405697c4b61fe58a92fc0265c6588#r335811454

* DVC_ISATTY in tests

- fixes https://github.com/iterative/dvc/pull/2550/files/ad1f245107c405697c4b61fe58a92fc0265c6588#r335811099

* re-add and abstract `env2bool` to `dvc.utils`

- partially reverts commit 524a0df188f566fb3e103f6965f1b6ac9c530886
- fixes
  https://github.com/iterative/dvc/pull/2550/files/ad1f245107c405697c4b61fe58a92fc0265c6588#r335811639

* env2bool tidy

* rename DVC_ISATTY => DVC_IGNORE_ISATTY

- fixes https://github.com/iterative/dvc/pull/2550#discussion_r335947382
2019-10-20 00:54:06 +03:00
Nabanita Dash a0cac4a3c8 changed order of imports (#2636) 2019-10-19 01:14:24 +03:00
Nabanita Dash 3eabb65781 corrected import order (#2634) 2019-10-19 00:32:45 +03:00
Ruslan Kuprieiev 638aaa254e dvc: bump to 0.63.3 2019-10-18 14:32:16 +03:00
Ruslan Kuprieiev 566d7b48d0 lock: monkeypatch socket.getfqdn used by flufl.lock (#2628)
Workaround for slow and obsoleted gethostbyaddr used in
`socket.getfqdn()`. See [1], [2] and [3] for more info.

[1] https://bugs.python.org/issue5004
[2] https://github.com/iterative/dvc/issues/2582
[3] https://gitlab.com/warsaw/flufl.lock/merge_requests/12

Flufl.lock guys seem unresponsive, so we have to monkeypatch
this by ourselves, at least for now.

Fixes #2582
2019-10-18 12:32:22 +03:00
212 changed files with 2232 additions and 1583 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ test_patterns = [
]
exclude_patterns = [
'dvc/utils/compat.py'
]
[[analyzers]]
+6 -7
View File
@@ -1,9 +1,8 @@
* [ ] Have you followed the guidelines in our
[Contributing document](https://dvc.org/doc/user-guide/contributing)?
* [ ] ❗ Have you followed the guidelines in the [Contributing to DVC](https://dvc.org/doc/user-guide/contributing/core) list?
* [ ] Does your PR affect documented changes or does it add new functionality
that should be documented? If yes, have you created a PR for
[dvc.org](https://github.com/iterative/dvc.org) documenting it or at
least opened an issue for it? If so, please add a link to it.
* [ ] 📖 Check this box if this PR **does not** require [documentation](https://dvc.org/doc) updates, or if it does **and** you have created a separate PR in [dvc.org](https://github.com/iterative/dvc.org) with such updates (or at least opened an issue about it in that repo). Please link below to your PR (or issue) in the [dvc.org](https://github.com/iterative/dvc.org) repo.
* [ ] ❌ Have you checked DeepSource, CodeClimate, and other sanity checks below? We consider their findings recommendatory and don't expect everything to be addresses. Please review them carefully and fix those that actually improve code or fix bugs.
Thank you for the contribution - we'll try to review it as soon as possible. 🙏
-----
+5
View File
@@ -0,0 +1,5 @@
# Config for https://github.com/apps/release-drafter
branches:
- master
template: |
$CHANGES
+1
View File
@@ -4,6 +4,7 @@ neatlynx/__pycache__
cache
*.pyc
.env/
.env2.7/
cache
.dvc.conf.lock
+5
View File
@@ -9,3 +9,8 @@ repos:
hooks:
- id: flake8
language_version: python3
- repo: https://github.com/asottile/reorder_python_imports
rev: v1.8.0
hooks:
- id: reorder-python-imports
language_version: python3
+21
View File
@@ -0,0 +1,21 @@
---
restylers:
- name: black
image: restyled/restyler-black:v19.3b0
command:
- black
arguments: []
include:
- "**/*.py"
interpreters:
- python
- name: reorder-python-imports
image: restyled/restyler-reorder-python-imports:v1.6.0
command:
- reorder-python-imports
- "--exit-zero-even-if-changed"
arguments: []
include:
- "**/*.py"
interpreters:
- python
+1 -1
View File
@@ -1 +1 @@
### See our contribution guide at [dvc.org](https://dvc.org/doc/user-guide/contributing#contributing).
### See our contribution guide at [dvc.org](https://dvc.org/doc/user-guide/contributing/core).
+2 -16
View File
@@ -134,14 +134,9 @@ Homebrew
.. code-block:: bash
brew install iterative/homebrew-dvc/dvc
brew install dvc
or:
.. code-block:: bash
brew cask install iterative/homebrew-dvc/dvc
Conda (Anaconda)
----------------
@@ -173,15 +168,6 @@ Fedora / CentOS (rpm)
sudo yum update
sudo yum install dvc
Arch Linux (AUR)
^^^^^^^^^^^^^^^^
*Unofficial package*, any inquiries regarding the AUR package,
`refer to the maintainer <https://github.com/mroutis/pkgbuilds>`_.
.. code-block:: bash
yay -S dvc
Related technologies
====================
@@ -202,7 +188,7 @@ Related technologies
Contributing
============
Contributions are welcome! Please see our `Contributing Guide <https://dvc.org/doc/user-guide/contributing/>`_ for more
Contributions are welcome! Please see our `Contributing Guide <https://dvc.org/doc/user-guide/contributing/core>`_ for more
details.
.. image:: https://sourcerer.io/fame/efiop/iterative/dvc/images/0
-2
View File
@@ -3,13 +3,11 @@ DVC
----
Make your data science projects reproducible and shareable.
"""
from __future__ import unicode_literals
import warnings
import dvc.logger
from dvc.version import __version__ # noqa: F401
-1
View File
@@ -1,5 +1,4 @@
"""Main entry point for dvc command line tool."""
from __future__ import unicode_literals
import sys
+8 -35
View File
@@ -1,15 +1,14 @@
"""Collect and send usage analytics"""
from __future__ import unicode_literals
from dvc.utils.compat import str
import os
import json
import errno
import json
import logging
import os
from dvc import __version__
from dvc.utils import env2bool
from dvc.utils.compat import str
logger = logging.getLogger(__name__)
@@ -205,45 +204,19 @@ class Analytics(object):
json.dump(self.info, fobj)
return fobj.name
@staticmethod
def _is_enabled_config(config):
from dvc.config import Config
core = config.config.get(Config.SECTION_CORE, {})
return core.get(Config.SECTION_CORE_ANALYTICS, True)
@staticmethod
def _get_current_config():
from dvc.config import Config
from dvc.repo import Repo
from dvc.exceptions import NotDvcRepoError
try:
dvc_dir = Repo.find_dvc_dir()
config = Config(dvc_dir)
except NotDvcRepoError:
config = Config(validate=False)
return config
@staticmethod
def is_enabled(cmd=None):
from dvc.config import Config, to_bool
from dvc.command.daemon import CmdDaemonBase
if os.getenv("DVC_TEST"):
if env2bool("DVC_TEST"):
return False
if isinstance(cmd, CmdDaemonBase):
return False
config = (
Analytics._get_current_config()
if cmd is None or not hasattr(cmd, "config")
else cmd.config
)
assert config is not None
enabled = Analytics._is_enabled_config(config)
core = Config(validate=False).config.get(Config.SECTION_CORE, {})
enabled = to_bool(core.get(Config.SECTION_CORE_ANALYTICS, "true"))
logger.debug(
"Analytics is {}.".format("enabled" if enabled else "disabled")
)
+1 -1
View File
@@ -1,5 +1,5 @@
from contextlib import contextmanager
import os
from contextlib import contextmanager
try:
from contextlib import _GeneratorContextManager as GCM
+36 -2
View File
@@ -1,12 +1,13 @@
"""Manages cache of a dvc repo."""
from __future__ import unicode_literals
import os
from collections import defaultdict
from funcy import cached_property
from dvc.utils.compat import builtin_str
from dvc.config import Config
from dvc.utils.compat import builtin_str
class CacheConfig(object):
@@ -103,3 +104,36 @@ class Cache(object):
ssh = _make_remote_property(Config.SECTION_CACHE_SSH)
hdfs = _make_remote_property(Config.SECTION_CACHE_HDFS)
azure = _make_remote_property(Config.SECTION_CACHE_AZURE)
class NamedCache(object):
def __init__(self):
self._items = defaultdict(lambda: defaultdict(set))
self.external = defaultdict(set)
@classmethod
def make(cls, scheme, checksum, name):
cache = cls()
cache.add(scheme, checksum, name)
return cache
def __getitem__(self, key):
return self._items[key]
def add(self, scheme, checksum, name):
self._items[scheme][checksum].add(name)
def add_external(self, url, rev, path):
self.external[url, rev].add(path)
def update(self, cache, suffix=""):
for scheme, src in cache._items.items():
dst = self._items[scheme]
for checksum, names in src.items():
if suffix:
dst[checksum].update(n + suffix for n in names)
else:
dst[checksum].update(names)
for repo_pair, files in cache.external.items():
self.external[repo_pair].update(files)
+22 -23
View File
@@ -1,42 +1,41 @@
"""Command line interface for dvc."""
from __future__ import print_function
from __future__ import unicode_literals
import sys
import argparse
import logging
import sys
from dvc.command.base import fix_subparsers
import dvc.command.init as init
import dvc.command.add as add
import dvc.command.cache as cache
import dvc.command.checkout as checkout
import dvc.command.commit as commit
import dvc.command.config as config
import dvc.command.daemon as daemon
import dvc.command.data_sync as data_sync
import dvc.command.destroy as destroy
import dvc.command.diff as diff
import dvc.command.gc as gc
import dvc.command.get as get
import dvc.command.get_url as get_url
import dvc.command.destroy as destroy
import dvc.command.remove as remove
import dvc.command.move as move
import dvc.command.unprotect as unprotect
import dvc.command.run as run
import dvc.command.repro as repro
import dvc.command.data_sync as data_sync
import dvc.command.gc as gc
import dvc.command.add as add
import dvc.command.imp as imp
import dvc.command.imp_url as imp_url
import dvc.command.config as config
import dvc.command.checkout as checkout
import dvc.command.remote as remote
import dvc.command.cache as cache
import dvc.command.metrics as metrics
import dvc.command.init as init
import dvc.command.install as install
import dvc.command.root as root
import dvc.command.lock as lock
import dvc.command.metrics as metrics
import dvc.command.move as move
import dvc.command.pipeline as pipeline
import dvc.command.daemon as daemon
import dvc.command.commit as commit
import dvc.command.remote as remote
import dvc.command.remove as remove
import dvc.command.repro as repro
import dvc.command.root as root
import dvc.command.run as run
import dvc.command.tag as tag
import dvc.command.diff as diff
import dvc.command.version as version
import dvc.command.unprotect as unprotect
import dvc.command.update as update
import dvc.command.version as version
from dvc.command.base import fix_subparsers
from dvc.exceptions import DvcParserError
+22 -19
View File
@@ -3,8 +3,10 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
from dvc.exceptions import RecursiveAddingWhileUsingFilename
logger = logging.getLogger(__name__)
@@ -14,49 +16,50 @@ class CmdAdd(CmdBase):
def run(self):
try:
if len(self.args.targets) > 1 and self.args.file:
raise DvcException("can't use '--file' with multiple targets")
raise RecursiveAddingWhileUsingFilename()
for target in self.args.targets:
self.repo.add(
target,
recursive=self.args.recursive,
no_commit=self.args.no_commit,
fname=self.args.file,
)
self.repo.add(
self.args.targets,
recursive=self.args.recursive,
no_commit=self.args.no_commit,
fname=self.args.file,
)
except DvcException:
logger.exception("failed to add file")
logger.exception("")
return 1
return 0
def add_parser(subparsers, parent_parser):
ADD_HELP = "Take data files or directories under DVC control."
ADD_HELP = "Track data files or directories with DVC."
add_parser = subparsers.add_parser(
parser = subparsers.add_parser(
"add",
parents=[parent_parser],
description=append_doc_link(ADD_HELP, "add"),
help=ADD_HELP,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
add_parser.add_argument(
parser.add_argument(
"-R",
"--recursive",
action="store_true",
default=False,
help="Recursively add each file under the directory.",
help="Recursively add files under directory targets.",
)
add_parser.add_argument(
parser.add_argument(
"--no-commit",
action="store_true",
default=False,
help="Don't put files/directories into cache.",
)
add_parser.add_argument(
"-f", "--file", help="Specify name of the DVC-file it generates."
parser.add_argument(
"-f",
"--file",
help="Specify name of the DVC-file this command will generate.",
)
add_parser.add_argument(
parser.add_argument(
"targets", nargs="+", help="Input files/directories to add."
)
add_parser.set_defaults(func=CmdAdd)
parser.set_defaults(func=CmdAdd)
+4 -3
View File
@@ -1,8 +1,9 @@
from __future__ import unicode_literals
import colorama
import logging
import colorama
logger = logging.getLogger(__name__)
@@ -26,7 +27,7 @@ def append_doc_link(help_message, path):
if not path:
return help_message
doc_base = "https://man.dvc.org/"
return "{message}\ndocumentation: {blue}{base}{path}{nc}".format(
return "{message}\nDocumentation: <{blue}{base}{path}{nc}>".format(
message=help_message,
base=doc_base,
path=path,
@@ -57,7 +58,7 @@ class CmdBase(object):
# Abstract methods that have to be implemented by any inheritance class
def run(self):
pass
raise NotImplementedError
class CmdBaseNoRepo(CmdBase):
+3 -2
View File
@@ -2,9 +2,10 @@ from __future__ import unicode_literals
import argparse
from dvc.command.base import fix_subparsers, append_doc_link
from dvc.command.config import CmdConfig
from dvc.cache import CacheConfig
from dvc.command.base import append_doc_link
from dvc.command.base import fix_subparsers
from dvc.command.config import CmdConfig
class CmdCacheDir(CmdConfig):
+8 -11
View File
@@ -2,21 +2,18 @@ from __future__ import unicode_literals
import argparse
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
class CmdCheckout(CmdBase):
def run(self):
if not self.args.targets:
self.repo.checkout(force=self.args.force)
else:
for target in self.args.targets:
self.repo.checkout(
target=target,
with_deps=self.args.with_deps,
force=self.args.force,
recursive=self.args.recursive,
)
self.repo.checkout(
targets=self.args.targets,
with_deps=self.args.with_deps,
force=self.args.force,
recursive=self.args.recursive,
)
return 0
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBaseNoRepo
from dvc.config import Config
+2 -1
View File
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
from dvc.command.base import CmdBaseNoRepo, fix_subparsers
from dvc.command.base import CmdBaseNoRepo
from dvc.command.base import fix_subparsers
class CmdDaemonBase(CmdBaseNoRepo):
+2 -1
View File
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
+3 -2
View File
@@ -4,7 +4,8 @@ import argparse
import logging
import dvc.prompt as prompt
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
@@ -24,7 +25,7 @@ class CmdDestroy(CmdBase):
if not self.args.force and not prompt.confirm(statement):
raise DvcException(
"cannot destroy without a confirmation from the user."
" Use '-f' to force."
" Use `-f` to force."
)
self.repo.destroy()
+3 -2
View File
@@ -1,14 +1,15 @@
from __future__ import unicode_literals
import argparse
import logging
import humanize
import inflect
import logging
from funcy import compact
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
+23 -13
View File
@@ -1,11 +1,12 @@
from __future__ import unicode_literals
import argparse
import os
import dvc.prompt as prompt
import logging
import os
from dvc.command.base import CmdBase, append_doc_link
import dvc.prompt as prompt
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
logger = logging.getLogger(__name__)
@@ -13,17 +14,19 @@ logger = logging.getLogger(__name__)
class CmdGC(CmdBase):
def run(self):
msg = "this will remove all cache except the cache that is used in "
if not self.args.all_branches and not self.args.all_tags:
msg += "the current git branch"
elif self.args.all_branches and not self.args.all_tags:
msg += "all git branches"
elif not self.args.all_branches and self.args.all_tags:
msg += "all git tags"
else:
msg += "all git branches and all git tags"
msg = "This will remove all cache except items used in "
if self.args.repos is not None and len(self.args.repos) > 0:
msg += "the working tree"
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"
if self.args.repos:
msg += " of the current and the following repos:"
for repo_path in self.args.repos:
@@ -40,6 +43,7 @@ class CmdGC(CmdBase):
self.repo.gc(
all_branches=self.args.all_branches,
all_tags=self.args.all_tags,
all_commits=self.args.all_commits,
cloud=self.args.cloud,
remote=self.args.remote,
force=self.args.force,
@@ -76,6 +80,12 @@ def add_parser(subparsers, parent_parser):
default=False,
help="Keep data files for all git tags.",
)
gc_parser.add_argument(
"--all-commits",
action="store_true",
default=False,
help=argparse.SUPPRESS,
)
gc_parser.add_argument(
"-c",
"--cloud",
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from .base import append_doc_link
from .base import CmdBaseNoRepo
from dvc.exceptions import DvcException
from .base import CmdBaseNoRepo, append_doc_link
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from .base import append_doc_link
from .base import CmdBaseNoRepo
from dvc.exceptions import DvcException
from .base import CmdBaseNoRepo, append_doc_link
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
+3 -2
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
@@ -19,7 +20,7 @@ class CmdImportUrl(CmdBase):
except DvcException:
logger.exception(
"failed to import {}. You could also try downloading "
"it manually and adding it with `dvc add` command.".format(
"it manually, and adding it with `dvc add`.".format(
self.args.url
)
)
+3 -2
View File
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBaseNoRepo
logger = logging.getLogger(__name__)
@@ -53,7 +54,7 @@ def add_parser(subparsers, parent_parser):
action="store_true",
default=False,
help=(
"Overwrite existing '.dvc' directory. "
"Overwrite existing '.dvc/' directory. "
"This operation removes local cache."
),
)
+2 -1
View File
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
+5 -2
View File
@@ -3,8 +3,11 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.exceptions import DvcException, BadMetricError
from dvc.command.base import CmdBase, fix_subparsers, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.command.base import fix_subparsers
from dvc.exceptions import BadMetricError
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
+5 -4
View File
@@ -1,13 +1,14 @@
from __future__ import unicode_literals
from dvc.utils import relpath
from dvc.utils.compat import str
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.command.base import fix_subparsers
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, fix_subparsers, append_doc_link
from dvc.utils import relpath
from dvc.utils.compat import str
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link, fix_subparsers
from dvc.command.base import append_doc_link
from dvc.command.base import fix_subparsers
from dvc.command.config import CmdConfig
from dvc.remote.config import RemoteConfig
+3 -2
View File
@@ -4,8 +4,9 @@ import argparse
import logging
import dvc.prompt as prompt
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
@@ -28,7 +29,7 @@ class CmdRemove(CmdBase):
raise DvcException(
"Cannot purge without a confirmation from the user."
" Use '-f' to force."
" Use `-f` to force."
)
def run(self):
+3 -2
View File
@@ -1,10 +1,11 @@
from __future__ import unicode_literals
import argparse
import os
import logging
import os
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.command.metrics import show_metrics
from dvc.command.status import CmdDataStatus
from dvc.exceptions import DvcException
+2 -1
View File
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBaseNoRepo
from dvc.utils import relpath
logger = logging.getLogger(__name__)
+5 -4
View File
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import CmdBase, append_doc_link
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
@@ -27,9 +28,9 @@ class CmdRun(CmdBase):
]
): # pragma: no cover
logger.error(
"too few arguments. Specify at least one: '-d', '-o', '-O',"
" '-m', '-M', '--outs-persist', '--outs-persist-no-cache',"
" 'command'."
"too few arguments. Specify at least one: `-d`, `-o`, `-O`, "
"`-m`, `-M`, `--outs-persist`, `--outs-persist-no-cache`, "
"`command`."
)
return 1
+4 -2
View File
@@ -1,8 +1,10 @@
import logging
from dvc.utils import to_yaml_string
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.command.base import fix_subparsers
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, fix_subparsers, append_doc_link
from dvc.utils import to_yaml_string
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
+2 -1
View File
@@ -3,8 +3,9 @@ from __future__ import unicode_literals
import argparse
import logging
from dvc.command.base import append_doc_link
from dvc.command.base import CmdBase
from dvc.exceptions import DvcException
from dvc.command.base import CmdBase, append_doc_link
logger = logging.getLogger(__name__)
+10 -8
View File
@@ -1,24 +1,24 @@
from __future__ import unicode_literals
import argparse
import itertools
import logging
import os
import platform
import argparse
import logging
import uuid
import itertools
try:
import psutil
except ImportError:
psutil = None
from dvc.utils import is_binary
from dvc.utils import is_binary, relpath
from dvc.utils.compat import pathlib
from dvc.command.base import CmdBaseNoRepo, append_doc_link
from dvc.version import __version__
from dvc.exceptions import DvcException, NotDvcRepoError
from dvc.system import System
from dvc.utils.pkg import PKG
logger = logging.getLogger(__name__)
@@ -31,17 +31,18 @@ class CmdVersion(CmdBaseNoRepo):
python_version = platform.python_version()
platform_type = platform.platform()
binary = is_binary()
info = (
"DVC version: {dvc_version}\n"
"Python version: {python_version}\n"
"Platform: {platform_type}\n"
"Binary: {binary}\n"
"Package: {package}\n"
).format(
dvc_version=dvc_version,
python_version=python_version,
platform_type=platform_type,
binary=binary,
package=PKG,
)
try:
@@ -60,8 +61,9 @@ class CmdVersion(CmdBaseNoRepo):
logger.warning(
"Unable to detect supported link types, as cache "
"directory '{}' doesn't exist. It is usually auto-created "
"by commands such as `dvc add/fetch/pull/run/import`, but "
"you could create it manually to enable this check."
"by commands such as `dvc add/fetch/pull/run/import`, "
"but you could create it manually to enable this "
"check.".format(relpath(repo.cache.local.cache_dir))
)
if psutil:
+14 -8
View File
@@ -1,18 +1,24 @@
"""DVC config objects."""
from __future__ import unicode_literals
from dvc.utils.compat import str, open
import os
import re
import copy
import errno
import configobj
import logging
import os
import re
from schema import Schema, Optional, And, Use, Regex, SchemaError
from dvc.exceptions import DvcException, NotDvcRepoError
import configobj
from schema import And
from schema import Optional
from schema import Regex
from schema import Schema
from schema import SchemaError
from schema import Use
from dvc.exceptions import DvcException
from dvc.exceptions import NotDvcRepoError
from dvc.utils.compat import open
from dvc.utils.compat import str
logger = logging.getLogger(__name__)
+4 -4
View File
@@ -1,15 +1,15 @@
"""Launch `dvc daemon` command in a separate detached process."""
from __future__ import unicode_literals
import os
import sys
import inspect
import logging
import os
import sys
from subprocess import Popen
from dvc.env import DVC_DAEMON
from dvc.utils import is_binary, fix_env
from dvc.utils import fix_env
from dvc.utils import is_binary
from dvc.utils.compat import cast_bytes_py2
+7 -5
View File
@@ -1,14 +1,16 @@
"""Draws DAG in ASCII."""
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import unicode_literals
import sys
import math
import sys
from grandalf.graphs import Vertex, Edge, Graph
from grandalf.graphs import Edge
from grandalf.graphs import Graph
from grandalf.graphs import Vertex
from grandalf.layouts import SugiyamaLayout
from grandalf.routing import route_with_lines, EdgeViewer
from grandalf.routing import EdgeViewer
from grandalf.routing import route_with_lines
class VertexViewer(object):
+12 -12
View File
@@ -1,10 +1,10 @@
"""Manages dvc remotes that user can use with push/pull/status comamnds."""
from __future__ import unicode_literals
import logging
from dvc.config import Config, NoRemoteError
from dvc.config import Config
from dvc.config import NoRemoteError
from dvc.remote import Remote
@@ -45,11 +45,11 @@ class DataCloud(object):
def _init_remote(self, remote):
return Remote(self.repo, name=remote)
def push(self, targets, jobs=None, remote=None, show_checksums=False):
def push(self, cache, jobs=None, remote=None, show_checksums=False):
"""Push data items in a cloud-agnostic way.
Args:
targets (list): list of targets to push to the cloud.
cache (NamedCache): named checksums to push to the cloud.
jobs (int): number of jobs that can be running simultaneously.
remote (dvc.remote.base.RemoteBASE): optional remote to push to.
By default remote from core.remote config option is used.
@@ -57,17 +57,17 @@ class DataCloud(object):
information messages.
"""
return self.repo.cache.local.push(
targets,
cache,
jobs=jobs,
remote=self.get_remote(remote, "push"),
show_checksums=show_checksums,
)
def pull(self, targets, jobs=None, remote=None, show_checksums=False):
def pull(self, cache, jobs=None, remote=None, show_checksums=False):
"""Pull data items in a cloud-agnostic way.
Args:
targets (list): list of targets to pull from the cloud.
cache (NamedCache): named checksums to pull from the cloud.
jobs (int): number of jobs that can be running simultaneously.
remote (dvc.remote.base.RemoteBASE): optional remote to pull from.
By default remote from core.remote config option is used.
@@ -75,25 +75,25 @@ class DataCloud(object):
information messages.
"""
return self.repo.cache.local.pull(
targets,
cache,
jobs=jobs,
remote=self.get_remote(remote, "pull"),
show_checksums=show_checksums,
)
def status(self, targets, jobs=None, remote=None, show_checksums=False):
def status(self, cache, jobs=None, remote=None, show_checksums=False):
"""Check status of data items in a cloud-agnostic way.
Args:
targets (list): list of targets to check status for.
cache (NamedCache): named checksums to check status for.
jobs (int): number of jobs that can be running simultaneously.
remote (dvc.remote.base.RemoteBASE): optional remote to compare
targets to. By default remote from core.remote config option
cache to. By default remote from core.remote config option
is used.
show_checksums (bool): show checksums instead of file names in
information messages.
"""
remote = self.get_remote(remote, "status")
return self.repo.cache.local.status(
targets, jobs=jobs, remote=remote, show_checksums=show_checksums
cache, jobs=jobs, remote=remote, show_checksums=show_checksums
)
+6 -8
View File
@@ -2,20 +2,18 @@ from __future__ import unicode_literals
import schema
from dvc.scheme import Schemes
import dvc.output as output
from dvc.output.base import OutputBase
from dvc.dependency.s3 import DependencyS3
from .repo import DependencyREPO
from dvc.dependency.gs import DependencyGS
from dvc.dependency.local import DependencyLOCAL
from dvc.dependency.hdfs import DependencyHDFS
from dvc.dependency.ssh import DependencySSH
from dvc.dependency.http import DependencyHTTP
from dvc.dependency.https import DependencyHTTPS
from .repo import DependencyREPO
from dvc.dependency.local import DependencyLOCAL
from dvc.dependency.s3 import DependencyS3
from dvc.dependency.ssh import DependencySSH
from dvc.output.base import OutputBase
from dvc.remote import Remote
from dvc.scheme import Schemes
DEPS = [
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.output.gs import OutputGS
from dvc.dependency.base import DependencyBase
from dvc.output.gs import OutputGS
class DependencyGS(DependencyBase, OutputGS):
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.output.hdfs import OutputHDFS
from dvc.dependency.base import DependencyBase
from dvc.output.hdfs import OutputHDFS
class DependencyHDFS(DependencyBase, OutputHDFS):
+1 -1
View File
@@ -1,8 +1,8 @@
from __future__ import unicode_literals
from dvc.dependency.base import DependencyBase
from dvc.output.base import OutputBase
from dvc.remote.http import RemoteHTTP
from dvc.dependency.base import DependencyBase
class DependencyHTTP(DependencyBase, OutputBase):
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.remote.https import RemoteHTTPS
from .http import DependencyHTTP
from dvc.remote.https import RemoteHTTPS
class DependencyHTTPS(DependencyHTTP):
+8 -4
View File
@@ -1,15 +1,14 @@
from __future__ import unicode_literals
import copy
from contextlib import contextmanager
from funcy import merge
from schema import Optional
from contextlib import contextmanager
from dvc.external_repo import external_repo
from dvc.utils.compat import str
from .local import DependencyLOCAL
from dvc.external_repo import external_repo
from dvc.utils.compat import str
class DependencyREPO(DependencyLOCAL):
@@ -35,6 +34,11 @@ class DependencyREPO(DependencyLOCAL):
def is_in_repo(self):
return False
@property
def repo_pair(self):
d = self.def_repo
return d[self.PARAM_URL], d[self.PARAM_REV_LOCK] or d[self.PARAM_REV]
def __str__(self):
return "{} ({})".format(self.def_path, self.def_repo[self.PARAM_URL])
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.output.s3 import OutputS3
from dvc.dependency.base import DependencyBase
from dvc.output.s3 import OutputS3
class DependencyS3(DependencyBase, OutputS3):
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.output.ssh import OutputSSH
from dvc.dependency.base import DependencyBase
from dvc.output.ssh import OutputSSH
class DependencySSH(DependencyBase, OutputSSH):
+10 -7
View File
@@ -1,12 +1,11 @@
"""Exceptions raised by the dvc."""
from __future__ import unicode_literals
from dvc.utils.compat import str, builtin_str
import traceback
from dvc.utils import relpath
from dvc.utils.compat import builtin_str
from dvc.utils.compat import str
class DvcException(Exception):
@@ -179,7 +178,7 @@ class ConfirmRemoveError(DvcException):
def __init__(self, path):
super(ConfirmRemoveError, self).__init__(
"unable to remove '{}' without a confirmation from the user. Use "
"'-f' to force.".format(path)
"`-f` to force.".format(path)
)
@@ -209,7 +208,7 @@ class NoMetricsError(DvcException):
def __init__(self):
super(NoMetricsError, self).__init__(
"no metric files in this repository. "
"Use 'dvc metrics add' to add a metric file to track."
"Use `dvc metrics add` to add a metric file to track."
)
@@ -226,7 +225,7 @@ class StageFileCorruptedError(DvcException):
class RecursiveAddingWhileUsingFilename(DvcException):
def __init__(self):
super(RecursiveAddingWhileUsingFilename, self).__init__(
"using fname with recursive is not allowed."
"cannot use `fname` with multiple targets or `-R|--recursive`"
)
@@ -247,7 +246,7 @@ class OverlappingOutputPathsError(DvcException):
class CheckoutErrorSuggestGit(DvcException):
def __init__(self, target, cause):
super(CheckoutErrorSuggestGit, self).__init__(
"Did you mean 'git checkout {}'?".format(target), cause=cause
"Did you mean `git checkout {}`?".format(target), cause=cause
)
@@ -325,3 +324,7 @@ class CheckoutError(DvcException):
"forget to fetch?".format("\n".join(targets))
)
super(CheckoutError, self).__init__(m)
class CollectCacheError(DvcException):
pass
+1 -1
View File
@@ -2,9 +2,9 @@ from __future__ import unicode_literals
import os
import tempfile
from contextlib import contextmanager
from distutils.dir_util import copy_tree
from contextlib import contextmanager
from funcy import retry
from dvc.utils import remove
+20 -8
View File
@@ -2,13 +2,12 @@ from __future__ import unicode_literals
import logging
import os
from pathspec import PathSpec
from pathspec.patterns import GitWildMatchPattern
from dvc.utils import relpath
from dvc.utils.compat import open
logger = logging.getLogger(__name__)
@@ -20,13 +19,13 @@ class DvcIgnore(object):
class DvcIgnorePatterns(DvcIgnore):
def __init__(self, ignore_file_path):
def __init__(self, ignore_file_path, tree):
assert os.path.isabs(ignore_file_path)
self.ignore_file_path = ignore_file_path
self.dirname = os.path.normpath(os.path.dirname(ignore_file_path))
with open(ignore_file_path, encoding="utf-8") as fobj:
with tree.open(ignore_file_path, encoding="utf-8") as fobj:
self.ignore_spec = PathSpec.from_lines(GitWildMatchPattern, fobj)
def __call__(self, root, dirs, files):
@@ -47,6 +46,9 @@ class DvcIgnorePatterns(DvcIgnore):
return hash(self.ignore_file_path)
def __eq__(self, other):
if not isinstance(other, DvcIgnorePatterns):
return NotImplemented
return self.ignore_file_path == other.ignore_file_path
@@ -59,19 +61,29 @@ class DvcIgnoreDirs(DvcIgnore):
return dirs, files
def __hash__(self):
return hash(tuple(self.basenames))
def __eq__(self, other):
if not isinstance(other, DvcIgnoreDirs):
return NotImplemented
return self.basenames == other.basenames
class DvcIgnoreFilter(object):
def __init__(self, root_dir):
def __init__(self, root_dir, tree):
self.tree = tree
self.ignores = {DvcIgnoreDirs([".git", ".hg", ".dvc"])}
self._update(root_dir)
for root, dirs, _ in os.walk(root_dir):
for root, dirs, _ in self.tree.walk(root_dir, dvcignore=self):
for d in dirs:
self._update(os.path.join(root, d))
def _update(self, dirname):
ignore_file_path = os.path.join(dirname, DvcIgnore.DVCIGNORE_FILE)
if os.path.exists(ignore_file_path):
self.ignores.add(DvcIgnorePatterns(ignore_file_path))
if self.tree.exists(ignore_file_path):
self.ignores.add(DvcIgnorePatterns(ignore_file_path, self.tree))
def __call__(self, root, dirs, files):
for ignore in self.ignores:
+2 -2
View File
@@ -1,8 +1,8 @@
"""Use heuristics to guess if it is a text file or a binary file."""
from __future__ import unicode_literals
from dvc.utils.compat import is_py3, open
from dvc.utils.compat import is_py3
from dvc.utils.compat import open
# Based on https://eli.thegreenplace.net/2011/10/19/
# perls-guess-if-file-is-text-or-binary-implemented-in-python
+33 -4
View File
@@ -1,9 +1,8 @@
"""Manages dvc lock file."""
from __future__ import unicode_literals
import os
import hashlib
import os
import time
from datetime import timedelta
@@ -34,12 +33,31 @@ if is_py3:
"""
def __init__(self, lockfile, tmp_dir=None):
lifetime = timedelta(days=365) # Lock for good by default
import socket
self._tmp_dir = tmp_dir
if self._tmp_dir is not None:
makedirs(self._tmp_dir, exist_ok=True)
super(Lock, self).__init__(lockfile, lifetime=lifetime)
# NOTE: this is basically Lock.__init__ copy-paste, except that
# instead of using `socket.getfqdn()` we use `socket.gethostname()`
# to speed this up. We've seen [1] `getfqdn()` take ~5sec to return
# anything, which is way too slow. `gethostname()` is actually a
# fallback for `getfqdn()` when it is not able to resolve a
# canonical hostname through network. The claimfile that uses
# `self._hostname` is still usable, as it uses `pid` and random
# number to generate the resulting lock file name, which is unique
# enough for our application.
#
# [1] https://github.com/iterative/dvc/issues/2582
self._hostname = socket.gethostname()
self._lockfile = lockfile
self._lifetime = timedelta(days=365) # Lock for good by default
self._separator = flufl.lock.SEP
self._set_claimfile()
self._owned = True
self._retry_errnos = []
@property
def lockfile(self):
@@ -68,6 +86,17 @@ if is_py3:
self._tmp_dir, filename + ".lock"
)
# Fix for __del__ bug in flufl.lock [1] which is causing errors on
# Python shutdown [2].
# [1] https://gitlab.com/warsaw/flufl.lock/issues/7
# [2] https://github.com/iterative/dvc/issues/2573
def __del__(self):
try:
if self._owned:
self.finalize()
except ImportError:
pass
else:
import zc.lockfile
+52 -27
View File
@@ -1,15 +1,27 @@
"""Manages logging configuration for dvc repo."""
from __future__ import unicode_literals
from dvc.utils.compat import str, StringIO, RecursionError
from dvc.progress import Tqdm
import logging
import logging.handlers
import logging.config
import logging.handlers
import colorama
from dvc.progress import Tqdm
from dvc.utils.compat import RecursionError
from dvc.utils.compat import str
from dvc.utils.compat import StringIO
FOOTER = (
"\n{yellow}Having any troubles?{nc}"
" Hit us up at {blue}https://dvc.org/support{nc},"
" we are always happy to help!"
).format(
blue=colorama.Fore.BLUE,
nc=colorama.Fore.RESET,
yellow=colorama.Fore.YELLOW,
)
class LoggingException(Exception):
def __init__(self, record):
@@ -22,6 +34,11 @@ class ExcludeErrorsFilter(logging.Filter):
return record.levelno < logging.WARNING
class ExcludeInfoFilter(logging.Filter):
def filter(self, record):
return record.levelno < logging.INFO
class ColorFormatter(logging.Formatter):
"""Enable color support when logging to a terminal that supports it.
@@ -43,16 +60,6 @@ class ColorFormatter(logging.Formatter):
"CRITICAL": colorama.Fore.RED,
}
footer = (
"{yellow}Having any troubles?{nc}"
" Hit us up at {blue}https://dvc.org/support{nc},"
" we are always happy to help!"
).format(
blue=colorama.Fore.BLUE,
nc=colorama.Fore.RESET,
yellow=colorama.Fore.YELLOW,
)
def format(self, record):
if record.levelname == "INFO":
return record.msg
@@ -61,10 +68,7 @@ class ColorFormatter(logging.Formatter):
exception, stack_trace = self._parse_exc(record.exc_info)
return (
"{color}{levelname}{nc}: {description}"
"{stack_trace}\n"
"\n"
"{footer}"
"{color}{levelname}{nc}: {description}" "{stack_trace}\n"
).format(
color=self.color_code.get(record.levelname, ""),
nc=colorama.Fore.RESET,
@@ -72,7 +76,6 @@ class ColorFormatter(logging.Formatter):
description=self._description(record.msg, exception),
msg=record.msg,
stack_trace=stack_trace,
footer=self.footer,
)
return "{color}{levelname}{nc}: {msg}".format(
@@ -170,15 +173,25 @@ def setup(level=logging.INFO):
logging.config.dictConfig(
{
"version": 1,
"filters": {"exclude_errors": {"()": ExcludeErrorsFilter}},
"filters": {
"exclude_errors": {"()": ExcludeErrorsFilter},
"exclude_info": {"()": ExcludeInfoFilter},
},
"formatters": {"color": {"()": ColorFormatter}},
"handlers": {
"console": {
"console_info": {
"class": "dvc.logger.LoggerHandler",
"level": "INFO",
"formatter": "color",
"stream": "ext://sys.stdout",
"filters": ["exclude_errors"],
},
"console_debug": {
"class": "dvc.logger.LoggerHandler",
"level": "DEBUG",
"formatter": "color",
"stream": "ext://sys.stdout",
"filters": ["exclude_errors"],
"filters": ["exclude_info"],
},
"console_errors": {
"class": "dvc.logger.LoggerHandler",
@@ -190,15 +203,27 @@ def setup(level=logging.INFO):
"loggers": {
"dvc": {
"level": level,
"handlers": ["console", "console_errors"],
"handlers": [
"console_info",
"console_debug",
"console_errors",
],
},
"paramiko": {
"level": logging.CRITICAL,
"handlers": ["console", "console_errors"],
"handlers": [
"console_info",
"console_debug",
"console_errors",
],
},
"flufl.lock": {
"level": logging.CRITICAL,
"handlers": ["console", "console_errors"],
"handlers": [
"console_info",
"console_debug",
"console_errors",
],
},
},
}
+16 -6
View File
@@ -1,19 +1,26 @@
"""Main entry point for dvc CLI."""
from __future__ import unicode_literals
import logging
from dvc.cli import parse_args
from dvc.lock import LockError
from dvc.config import ConfigError
from dvc.analytics import Analytics
from dvc.exceptions import NotDvcRepoError, DvcParserError
from dvc.remote.pool import close_pools
from dvc.cli import parse_args
from dvc.config import ConfigError
from dvc.exceptions import DvcParserError
from dvc.exceptions import NotDvcRepoError
from dvc.external_repo import clean_repos
from dvc.lock import LockError
from dvc.logger import FOOTER
from dvc.remote.pool import close_pools
from dvc.utils.compat import is_py2
# 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
"".encode("idna")
logger = logging.getLogger("dvc")
@@ -75,6 +82,9 @@ def main(argv=None):
# so won't be reused by any other subsequent run anyway.
clean_repos()
if ret != 0:
logger.info(FOOTER)
Analytics().send_cmd(cmd, args, ret)
return ret
+6 -7
View File
@@ -2,20 +2,19 @@ from __future__ import unicode_literals
import schema
from dvc.scheme import Schemes
from dvc.utils.compat import urlparse, str
from dvc.output.base import OutputBase
from dvc.output.s3 import OutputS3
from dvc.output.gs import OutputGS
from dvc.output.local import OutputLOCAL
from dvc.output.hdfs import OutputHDFS
from dvc.output.local import OutputLOCAL
from dvc.output.s3 import OutputS3
from dvc.output.ssh import OutputSSH
from dvc.remote import Remote
from dvc.remote.s3 import RemoteS3
from dvc.remote.hdfs import RemoteHDFS
from dvc.remote.local import RemoteLOCAL
from dvc.remote.s3 import RemoteS3
from dvc.scheme import Schemes
from dvc.utils.compat import str
from dvc.utils.compat import urlparse
OUTS = [
OutputHDFS,
+24 -26
View File
@@ -3,12 +3,16 @@ from __future__ import unicode_literals
import logging
from copy import copy
from schema import Or, Optional
from schema import Optional
from schema import Or
import dvc.prompt as prompt
from dvc.cache import NamedCache
from dvc.exceptions import CollectCacheError
from dvc.exceptions import DvcException
from dvc.utils.compat import str, urlparse
from dvc.remote.base import RemoteBASE
from dvc.utils.compat import str
from dvc.utils.compat import urlparse
logger = logging.getLogger(__name__)
@@ -286,7 +290,8 @@ class OutputBase(object):
def checkout(self, force=False, progress_callback=None, tag=None):
if not self.use_cache:
progress_callback(str(self.path_info), self.get_files_number())
if progress_callback:
progress_callback(str(self.path_info), self.get_files_number())
return None
if tag:
@@ -333,7 +338,7 @@ class OutputBase(object):
self.remote.unprotect(self.path_info)
def _collect_used_dir_cache(self, remote=None, force=False, jobs=None):
"""Get a list of `info`s retaled to the given directory.
"""Get a list of `info`s related to the given directory.
- Pull the directory entry from the remote cache if it was changed.
@@ -345,25 +350,19 @@ class OutputBase(object):
$ echo "bar" > directory/bar
$ dvc add directory
It will return something similar to the following list:
It will return a NamedCache like:
[
{ 'path': 'directory/foo', 'md5': 'c157a79031e1', ... },
{ 'path': 'directory/bar', 'md5': 'd3b07384d113', ... },
]
nc = NamedCache()
nc.add(self.scheme, 'c157a79031e1', 'directory/foo')
nc.add(self.scheme, 'd3b07384d113', 'directory/bar')
"""
ret = []
cache = NamedCache()
if self.cache.changed_cache_file(self.checksum):
try:
self.repo.cloud.pull(
[
{
self.remote.PARAM_CHECKSUM: self.checksum,
"name": str(self),
}
],
NamedCache.make("local", self.checksum, str(self)),
jobs=jobs,
remote=remote,
show_checksums=False,
@@ -378,20 +377,19 @@ class OutputBase(object):
"Would you like to continue? Use '-f' to force."
)
if not force and not prompt.confirm(msg.format(self.path_info)):
raise DvcException(
raise CollectCacheError(
"unable to fully collect used cache"
" without cache for directory '{}'".format(self)
)
else:
return ret
return cache
for entry in self.dir_cache:
info = copy(entry)
checksum = entry[self.remote.PARAM_CHECKSUM]
path_info = self.path_info / entry[self.remote.PARAM_RELPATH]
info["name"] = str(path_info)
ret.append(info)
cache.add(self.scheme, checksum, str(path_info))
return ret
return cache
def get_used_cache(self, **kwargs):
"""Get a dumpd of the given `out`, with an entry including the branch.
@@ -403,7 +401,7 @@ class OutputBase(object):
"""
if not self.use_cache:
return []
return NamedCache()
if not self.info:
logger.warning(
@@ -411,14 +409,14 @@ class OutputBase(object):
"not be collected. Use dvc repro to get your pipeline up to "
"date.".format(self, self.stage)
)
return []
return NamedCache()
ret = [{self.remote.PARAM_CHECKSUM: self.checksum, "name": str(self)}]
ret = NamedCache.make(self.scheme, self.checksum, str(self))
if not self.is_dir_checksum:
return ret
ret.extend(self._collect_used_dir_cache(**kwargs))
ret.update(self._collect_used_dir_cache(**kwargs))
return ret
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.remote.gs import RemoteGS
from dvc.output.s3 import OutputS3
from dvc.remote.gs import RemoteGS
class OutputGS(OutputS3):
+7 -5
View File
@@ -1,13 +1,15 @@
from __future__ import unicode_literals
import os
import logging
import os
from dvc.utils.compat import urlparse, fspath_py35, str
from dvc.istextfile import istextfile
from dvc.exceptions import DvcException
from dvc.remote.local import RemoteLOCAL
from dvc.istextfile import istextfile
from dvc.output.base import OutputBase
from dvc.remote.local import RemoteLOCAL
from dvc.utils.compat import fspath_py35
from dvc.utils.compat import str
from dvc.utils.compat import urlparse
logger = logging.getLogger(__name__)
@@ -26,7 +28,7 @@ class OutputLOCAL(OutputBase):
# so we should expect both posix and windows style paths.
# PathInfo accepts both, i.e. / works everywhere, \ only on win.
#
# FIXME: if we have Windows path containig / or posix one with \
# FIXME: if we have Windows path containing / or posix one with \
# then we have #2059 bug and can't really handle that.
p = self.REMOTE.path_cls(path)
if not p.is_absolute():
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.remote.s3 import RemoteS3
from dvc.output.base import OutputBase
from dvc.remote.s3 import RemoteS3
class OutputS3(OutputBase):
+90 -92
View File
@@ -1,20 +1,24 @@
from __future__ import unicode_literals, absolute_import
from __future__ import absolute_import
from __future__ import unicode_literals
import sys
import os
import posixpath
import sys
from funcy import cached_property
from dvc.utils.compat import str, builtin_str, basestring, is_py2
from dvc.utils.compat import pathlib, urlparse
from dvc.utils import relpath
from dvc.utils.compat import basestring
from dvc.utils.compat import builtin_str
from dvc.utils.compat import is_py2
from dvc.utils.compat import pathlib
from dvc.utils.compat import str
from dvc.utils.compat import urlparse
# On Python 2.7/Windows sys.getfilesystemencoding() is set to mbcs,
# which is lossy, thus we can't use that,
# see https://github.com/mcmtroffaes/pathlib2/issues/56.
from dvc.utils import relpath
if is_py2:
fs_encoding = "utf-8"
@@ -112,52 +116,80 @@ class PosixPathInfo(PathInfo, pathlib.PurePosixPath):
pass
class _URLPathInfo(PosixPathInfo):
def __str__(self):
return self.__fspath__()
__unicode__ = __str__
class _URLPathParents(object):
def __init__(self, pathcls, scheme, netloc, path):
self._scheme = scheme
self._netloc = netloc
self._parents = path.parents
self._pathcls = pathcls
def __init__(self, src):
self.src = src
self._parents = self.src._path.parents
def __len__(self):
return len(self._parents)
def __getitem__(self, idx):
return self._pathcls.from_parts(
scheme=self._scheme,
netloc=self._netloc,
path=self._parents[idx].fspath,
)
return self.src.replace(path=self._parents[idx])
def __repr__(self):
return "<{}.parents>".format(self._pathcls.__name__)
return "<{}.parents>".format(self.src)
class URLInfo(object):
DEFAULT_PORTS = {"http": 80, "https": 443, "ssh": 22, "hdfs": 0}
def __init__(self, url):
self.parsed = urlparse(url)
assert self.parsed.scheme != "remote"
p = urlparse(url)
assert not p.query and not p.params and not p.fragment
assert p.password is None
self.fill_parts(p.scheme, p.hostname, p.username, p.port, p.path)
@classmethod
def from_parts(
cls, scheme=None, netloc=None, host=None, user=None, port=None, path=""
cls, scheme=None, host=None, user=None, port=None, path="", netloc=None
):
assert scheme and (bool(host) ^ bool(netloc))
assert bool(host) ^ bool(netloc)
if netloc is None:
netloc = host
if user:
netloc = user + "@" + host
if port:
netloc += ":" + str(port)
return cls("{}://{}{}".format(scheme, netloc, path))
if netloc is not None:
return cls("{}://{}{}".format(scheme, netloc, path))
obj = cls.__new__(cls)
obj.fill_parts(scheme, host, user, port, path)
return obj
def fill_parts(self, scheme, host, user, port, path):
assert scheme != "remote"
assert isinstance(path, (basestring, _URLPathInfo))
self.scheme, self.host, self.user = scheme, host, user
self.port = int(port) if port else self.DEFAULT_PORTS.get(self.scheme)
if isinstance(path, _URLPathInfo):
self._spath = builtin_str(path)
self._path = path
else:
if path and path[0] != "/":
path = "/" + path
self._spath = path
@property
def _base_parts(self):
return (self.scheme, self.host, self.user, self.port)
@property
def parts(self):
return self._base_parts + self._path.parts
def replace(self, path=None):
return self.from_parts(*self._base_parts, path=path)
@cached_property
def url(self):
p = self.parsed
return "{}://{}{}".format(p.scheme, self.netloc, p.path)
return "{}://{}{}".format(self.scheme, self.netloc, self._spath)
def __str__(self):
return self.url
@@ -170,92 +202,60 @@ class URLInfo(object):
other = self.__class__(other)
return (
self.__class__ == other.__class__
and self.scheme == other.scheme
and self.netloc == other.netloc
and self._base_parts == other._base_parts
and self._path == other._path
)
def __hash__(self):
return hash(self.url)
return hash(self.parts)
def __div__(self, other):
p = self.parsed
new_path = posixpath.join(p.path, str(other))
if not new_path.startswith("/"):
new_path = "/" + new_path
new_url = "{}://{}{}".format(p.scheme, p.netloc, new_path)
return self.__class__(new_url)
return self.replace(path=posixpath.join(self._spath, other))
__truediv__ = __div__
def __getattr__(self, name):
# When deepcopy is called, it creates and object without __init__,
# self.parsed is not initialized and it causes infinite recursion.
# More on this special casing here:
# https://stackoverflow.com/a/47300262/298182
if name.startswith("__"):
raise AttributeError(name)
return getattr(self.parsed, name)
@cached_property
def netloc(self):
p = self.parsed
netloc = p.hostname
if p.username:
netloc = p.username + "@" + netloc
if p.port and int(p.port) != self.DEFAULT_PORTS.get(p.scheme):
netloc += ":" + str(p.port)
return netloc
@property
def port(self):
return self.parsed.port or self.DEFAULT_PORTS.get(self.parsed.scheme)
@property
def host(self):
return self.parsed.hostname
@property
def user(self):
return self.parsed.username
def path(self):
return self._spath
@cached_property
def _path(self):
return PosixPathInfo(self.parsed.path)
return _URLPathInfo(self._spath)
@property
def name(self):
return self._path.name
@property
def parts(self):
return (self.scheme, self.netloc) + self._path.parts
@cached_property
def netloc(self):
netloc = self.host
if self.user:
netloc = self.user + "@" + netloc
if self.port and int(self.port) != self.DEFAULT_PORTS.get(self.scheme):
netloc += ":" + str(self.port)
return netloc
@property
def bucket(self):
return self.parsed.netloc
return self.netloc
@property
def parent(self):
return self.from_parts(
scheme=self.scheme,
netloc=self.parsed.netloc,
path=self._path.parent.fspath,
)
return self.replace(path=self._path.parent)
@property
def parents(self):
return _URLPathParents(
type(self), self.scheme, self.parsed.netloc, self._path
)
return _URLPathParents(self)
def relative_to(self, other):
if isinstance(other, str):
other = URLInfo(other)
if self.scheme != other.scheme or self.netloc != other.netloc:
raise ValueError(
"'{}' does not start with '{}'".format(self, other)
)
if isinstance(other, basestring):
other = self.__class__(other)
if self.__class__ != other.__class__:
msg = "'{}' has incompatible class with '{}'".format(self, other)
raise ValueError(msg)
if self._base_parts != other._base_parts:
msg = "'{}' does not start with '{}'".format(self, other)
raise ValueError(msg)
return self._path.relative_to(other._path)
def isin(self, other):
@@ -263,14 +263,12 @@ class URLInfo(object):
other = self.__class__(other)
elif self.__class__ != other.__class__:
return False
return (
self.scheme == other.scheme
and self.netloc == other.netloc
and self._path.isin(other._path)
return self._base_parts == other._base_parts and self._path.isin(
other._path
)
class CloudURLInfo(URLInfo):
@property
def path(self):
return self.parsed.path.lstrip("/")
return self._spath.lstrip("/")
+25 -26
View File
@@ -1,33 +1,17 @@
"""Manages progress bars for dvc repo."""
from __future__ import print_function
import logging
from tqdm import tqdm
from concurrent.futures import ThreadPoolExecutor
import sys
from threading import RLock
from funcy import merge
from tqdm import tqdm
from dvc.utils import env2bool
logger = logging.getLogger(__name__)
class TqdmThreadPoolExecutor(ThreadPoolExecutor):
"""
Ensure worker progressbars are cleared away properly.
"""
def __enter__(self):
"""
Creates a blank initial dummy progress bar if needed so that workers
are forced to create "nested" bars.
"""
blank_bar = Tqdm(bar_format="Multi-Threaded:", leave=False)
if blank_bar.pos > 0:
# already nested - don't need a placeholder bar
blank_bar.close()
self.bar = blank_bar
return super(TqdmThreadPoolExecutor, self).__enter__()
def __exit__(self, *a, **k):
super(TqdmThreadPoolExecutor, self).__exit__(*a, **k)
self.bar.close()
tqdm.set_lock(RLock())
class Tqdm(tqdm):
@@ -54,6 +38,7 @@ class Tqdm(tqdm):
leave=False,
bar_format=None,
bytes=False, # pylint: disable=W0622
file=None,
**kwargs
):
"""
@@ -62,6 +47,9 @@ class Tqdm(tqdm):
desc : persists after `close()`
level : effective logging level for determining `disable`;
used only if `disable` is unspecified
disable : If (default: None), will be determined by logging level.
May be overridden to `True` due to non-TTY status.
Skip override by specifying env var `DVC_IGNORE_ISATTY`.
kwargs : anything accepted by `tqdm.tqdm()`
"""
kwargs = kwargs.copy()
@@ -71,15 +59,26 @@ class Tqdm(tqdm):
unit="B", unit_scale=True, unit_divisor=1024, miniters=1
)
kwargs = merge(bytes_defaults, kwargs)
if file is None:
file = sys.stderr
self.desc_persist = desc
# auto-disable based on `logger.level`
if disable is None:
disable = logger.getEffectiveLevel() > level
# auto-disable based on TTY
if (
not disable
and not env2bool("DVC_IGNORE_ISATTY")
and hasattr(file, "isatty")
):
disable = not file.isatty()
super(Tqdm, self).__init__(
iterable=iterable,
disable=disable,
leave=leave,
desc=desc,
bar_format="!",
lock_args=(False,),
**kwargs
)
if bar_format is None:
@@ -118,7 +117,7 @@ class Tqdm(tqdm):
if ncols_desc:
d["ncols_desc"] = ncols_desc
else:
# work-around for zero-width desc
# work-around for zero-width description
d["ncols_desc"] = 1
d["desc"] = 0
d["prefix"] = ""
return d
+2 -3
View File
@@ -1,10 +1,9 @@
"""Manages user prompts."""
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import unicode_literals
import sys
import logging
import sys
from getpass import getpass
from dvc.utils.compat import input
+4 -5
View File
@@ -1,16 +1,15 @@
from __future__ import unicode_literals
from .config import RemoteConfig
from dvc.remote.azure import RemoteAZURE
from dvc.remote.gs import RemoteGS
from dvc.remote.hdfs import RemoteHDFS
from dvc.remote.local import RemoteLOCAL
from dvc.remote.s3 import RemoteS3
from dvc.remote.ssh import RemoteSSH
from dvc.remote.http import RemoteHTTP
from dvc.remote.https import RemoteHTTPS
from dvc.remote.local import RemoteLOCAL
from dvc.remote.oss import RemoteOSS
from .config import RemoteConfig
from dvc.remote.s3 import RemoteS3
from dvc.remote.ssh import RemoteSSH
REMOTES = [
+7 -7
View File
@@ -1,20 +1,20 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import logging
import os
import re
import logging
from datetime import datetime, timedelta
from datetime import datetime
from datetime import timedelta
from funcy import cached_property
from dvc.scheme import Schemes
from dvc.utils.compat import urlparse
from dvc.progress import Tqdm
from dvc.config import Config
from dvc.remote.base import RemoteBASE
from dvc.path_info import CloudURLInfo
from dvc.progress import Tqdm
from dvc.remote.base import RemoteBASE
from dvc.scheme import Schemes
from dvc.utils.compat import urlparse
logger = logging.getLogger(__name__)
+38 -46
View File
@@ -1,30 +1,34 @@
from __future__ import unicode_literals
from operator import itemgetter
from multiprocessing import cpu_count
import itertools
import json
import logging
import tempfile
import itertools
from functools import partial
from concurrent.futures import ThreadPoolExecutor
from copy import copy
from dvc.ignore import DvcIgnore
from dvc.utils.compat import str, basestring, urlparse, FileNotFoundError
from dvc.remote.slow_link_detection import slow_link_guard
from functools import partial
from multiprocessing import cpu_count
from operator import itemgetter
import dvc.prompt as prompt
from dvc.config import Config
from dvc.exceptions import (
DvcException,
ConfirmRemoveError,
DvcIgnoreInCollectedDirError,
)
from dvc.exceptions import ConfirmRemoveError
from dvc.exceptions import DvcException
from dvc.exceptions import DvcIgnoreInCollectedDirError
from dvc.ignore import DvcIgnore
from dvc.path_info import PathInfo
from dvc.path_info import URLInfo
from dvc.progress import Tqdm
from dvc.utils import LARGE_DIR_SIZE, tmp_fname, move, relpath, makedirs
from dvc.remote.slow_link_detection import slow_link_guard
from dvc.state import StateNoop
from dvc.path_info import PathInfo, URLInfo
from dvc.utils import makedirs
from dvc.utils import relpath
from dvc.utils import tmp_fname
from dvc.utils.compat import basestring
from dvc.utils.compat import FileNotFoundError
from dvc.utils.compat import str
from dvc.utils.compat import urlparse
from dvc.utils.fs import move
from dvc.utils.http import open_url
logger = logging.getLogger(__name__)
@@ -177,27 +181,23 @@ class RemoteBASE(object):
file_infos = list(file_infos)
with ThreadPoolExecutor(max_workers=self.checksum_jobs) as executor:
tasks = executor.map(self.get_file_checksum, file_infos)
if len(file_infos) > LARGE_DIR_SIZE:
logger.info(
(
"Computing md5 for a large number of files. "
"This is only done once."
)
)
tasks = Tqdm(tasks, total=len(file_infos), unit="md5")
checksums = dict(zip(file_infos, tasks))
with Tqdm(
tasks,
total=len(file_infos),
unit="md5",
desc="Computing hashes (only done once)",
) as tasks:
checksums = dict(zip(file_infos, tasks))
return checksums
def _collect_dir(self, path_info):
file_infos = set()
for root, _dirs, files in self.walk(path_info):
if DvcIgnore.DVCIGNORE_FILE in files:
raise DvcIgnoreInCollectedDirError(root)
for fname in self.walk_files(path_info):
if DvcIgnore.DVCIGNORE_FILE == fname.name:
raise DvcIgnoreInCollectedDirError(fname.parent)
file_infos.update(path_info / root / fname for fname in files)
file_infos.add(fname)
checksums = {fi: self.state.get(fi) for fi in file_infos}
not_in_state = {
@@ -466,7 +466,8 @@ class RemoteBASE(object):
"""
return False
def walk(self, path_info):
def walk_files(self, path_info):
"""Return a generator with `PathInfo`s to all the files"""
raise NotImplementedError
@staticmethod
@@ -620,14 +621,11 @@ class RemoteBASE(object):
# We ignore all the non-cache looking files
pass
def gc(self, cinfos):
used = self.extract_used_local_checksums(cinfos)
def gc(self, named_cache):
used = self.extract_used_local_checksums(named_cache)
if self.scheme != "":
used |= {
info[self.PARAM_CHECKSUM]
for info in cinfos.get(self.scheme, [])
}
used.update(named_cache[self.scheme])
removed = False
for checksum in self.all():
@@ -834,11 +832,7 @@ class RemoteBASE(object):
self.state.save(path_info, checksum)
def _remove_redundant_files(self, path_info, dir_info, force):
existing_files = set(
path_info / root / fname
for root, _, files in self.walk(path_info)
for fname in files
)
existing_files = set(self.walk_files(path_info))
needed_files = {
path_info / entry[self.PARAM_RELPATH] for entry in dir_info
@@ -909,10 +903,8 @@ class RemoteBASE(object):
def _get_unpacked_dir_names(self, checksums):
return set()
def extract_used_local_checksums(self, cinfos):
from dvc.remote import RemoteLOCAL
used = {info[RemoteLOCAL.PARAM_CHECKSUM] for info in cinfos["local"]}
def extract_used_local_checksums(self, named_cache):
used = set(named_cache["local"])
unpacked = self._get_unpacked_dir_names(used)
return used | unpacked
+3 -2
View File
@@ -1,10 +1,11 @@
import os
import logging
import os
import posixpath
from dvc.config import Config
from dvc.config import ConfigError
from dvc.utils import relpath
from dvc.utils.compat import urlparse
from dvc.config import Config, ConfigError
logger = logging.getLogger(__name__)
+40 -4
View File
@@ -2,18 +2,55 @@ from __future__ import unicode_literals
import logging
from datetime import timedelta
from functools import wraps
from funcy import cached_property
from dvc.utils.compat import FileNotFoundError
from dvc.remote.base import RemoteBASE
from dvc.config import Config
from dvc.exceptions import DvcException
from dvc.path_info import CloudURLInfo
from dvc.remote.base import RemoteBASE
from dvc.scheme import Schemes
from dvc.utils.compat import FileNotFoundError # skipcq: PYL-W0622
logger = logging.getLogger(__name__)
def dynamic_chunk_size(func):
@wraps(func)
def wrapper(*args, **kwargs):
import requests
from google.cloud.storage.blob import Blob, _DEFAULT_CHUNKSIZE
# Default chunk size for gs is 100M, which might be too much for
# particular network (see [1]). So if we are getting ConnectionError,
# we should try lowering the chunk size until we reach the minimum
# allowed chunk size of 256K. Also note that `chunk_size` must be a
# multiple of 256K per the API specification.
#
# [1] https://github.com/iterative/dvc/issues/2572
# skipcq: PYL-W0212
multiplier = int(_DEFAULT_CHUNKSIZE / Blob._CHUNK_SIZE_MULTIPLE)
while True:
try:
# skipcq: PYL-W0212
chunk_size = Blob._CHUNK_SIZE_MULTIPLE * multiplier
return func(*args, chunk_size=chunk_size, **kwargs)
except requests.exceptions.ConnectionError:
multiplier = int(multiplier / 2)
if not multiplier:
raise
return wrapper
@dynamic_chunk_size
def _upload_to_bucket(bucket, from_file, to_info, **kwargs):
blob = bucket.blob(to_info.path, **kwargs)
blob.upload_from_filename(from_file)
class RemoteGS(RemoteBASE):
scheme = Schemes.GS
path_cls = CloudURLInfo
@@ -88,8 +125,7 @@ class RemoteGS(RemoteBASE):
def _upload(self, from_file, to_info, **_kwargs):
bucket = self.gs.bucket(to_info.bucket)
blob = bucket.blob(to_info.path)
blob.upload_from_filename(from_file)
_upload_to_bucket(bucket, from_file, to_info)
def _download(self, from_info, to_file, **_kwargs):
bucket = self.gs.bucket(from_info.bucket)
+14 -11
View File
@@ -1,21 +1,24 @@
from __future__ import unicode_literals
import os
import re
import io
import posixpath
import logging
from subprocess import Popen, PIPE
from contextlib import contextmanager, closing
import os
import posixpath
import re
from contextlib import closing
from contextlib import contextmanager
from subprocess import PIPE
from subprocess import Popen
from .base import RemoteBASE
from .base import RemoteCmdError
from .pool import get_connection
from dvc.config import Config
from dvc.scheme import Schemes
from dvc.utils.compat import urlparse, FileNotFoundError
from dvc.utils import fix_env, tmp_fname
from .pool import get_connection
from .base import RemoteBASE, RemoteCmdError
from dvc.utils import fix_env
from dvc.utils import tmp_fname
from dvc.utils.compat import FileNotFoundError
from dvc.utils.compat import urlparse
logger = logging.getLogger(__name__)
+29 -6
View File
@@ -1,19 +1,24 @@
from __future__ import unicode_literals
import logging
from funcy import cached_property
from dvc.config import Config
from dvc.config import ConfigError
from dvc.exceptions import DvcException
from dvc.progress import Tqdm
from dvc.remote.base import RemoteBASE
from dvc.scheme import Schemes
from dvc.utils.compat import open
from dvc.progress import Tqdm
from dvc.exceptions import DvcException
from dvc.config import Config, ConfigError
from dvc.remote.base import RemoteBASE
logger = logging.getLogger(__name__)
class RemoteHTTP(RemoteBASE):
scheme = Schemes.HTTP
SESSION_RETRIES = 5
SESSION_BACKOFF_FACTOR = 0.1
REQUEST_TIMEOUT = 10
CHUNK_SIZE = 2 ** 16
PARAM_CHECKSUM = "etag"
@@ -76,6 +81,24 @@ class RemoteHTTP(RemoteBASE):
return etag
@cached_property
def _session(self):
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retries = Retry(
total=self.SESSION_RETRIES,
backoff_factor=self.SESSION_BACKOFF_FACTOR,
)
session.mount("http://", HTTPAdapter(max_retries=retries))
session.mount("https://", HTTPAdapter(max_retries=retries))
return session
def _request(self, method, url, **kwargs):
import requests
@@ -83,7 +106,7 @@ class RemoteHTTP(RemoteBASE):
kwargs.setdefault("timeout", self.REQUEST_TIMEOUT)
try:
return requests.request(method, url, **kwargs)
return self._session.request(method, url, **kwargs)
except requests.exceptions.RequestException:
raise DvcException("could not perform a {} request".format(method))
+1 -2
View File
@@ -1,8 +1,7 @@
from __future__ import unicode_literals
from dvc.scheme import Schemes
from .http import RemoteHTTP
from dvc.scheme import Schemes
class RemoteHTTPS(RemoteHTTP):
+47 -69
View File
@@ -1,39 +1,38 @@
from __future__ import unicode_literals
from dvc.scheme import Schemes
from dvc.utils.compat import str, fspath_py35, open
import errno
import logging
import os
import stat
import errno
from shortuuid import uuid
import logging
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from dvc.system import System
from dvc.remote.base import (
RemoteBASE,
STATUS_MAP,
STATUS_NEW,
STATUS_DELETED,
STATUS_MISSING,
)
from dvc.utils import (
remove,
move,
copyfile,
tmp_fname,
file_md5,
walk_files,
relpath,
dvc_walk,
makedirs,
)
from dvc.config import Config
from dvc.exceptions import DvcException, DownloadError, UploadError
from dvc.progress import Tqdm, TqdmThreadPoolExecutor
from shortuuid import uuid
from dvc.config import Config
from dvc.exceptions import DownloadError
from dvc.exceptions import DvcException
from dvc.exceptions import UploadError
from dvc.path_info import PathInfo
from dvc.progress import Tqdm
from dvc.remote.base import RemoteBASE
from dvc.remote.base import STATUS_DELETED
from dvc.remote.base import STATUS_MAP
from dvc.remote.base import STATUS_MISSING
from dvc.remote.base import STATUS_NEW
from dvc.scheme import Schemes
from dvc.system import System
from dvc.utils import copyfile
from dvc.utils import file_md5
from dvc.utils import makedirs
from dvc.utils import relpath
from dvc.utils import remove
from dvc.utils import tmp_fname
from dvc.utils import walk_files
from dvc.utils.compat import fspath_py35
from dvc.utils.compat import open
from dvc.utils.compat import str
from dvc.utils.fs import move
logger = logging.getLogger(__name__)
@@ -145,11 +144,12 @@ class RemoteLOCAL(RemoteBASE):
def getsize(path_info):
return os.path.getsize(fspath_py35(path_info))
def walk(self, path_info):
return dvc_walk(path_info, self.repo.dvcignore)
def walk_files(self, path_info):
for fname in walk_files(path_info, self.repo.dvcignore):
yield PathInfo(fname)
def get_file_checksum(self, path_info):
return file_md5(fspath_py35(path_info))[0]
return file_md5(path_info)[0]
def remove(self, path_info):
if path_info.scheme != "local":
@@ -224,7 +224,7 @@ class RemoteLOCAL(RemoteBASE):
def _upload(
self, from_file, to_info, name=None, no_progress_bar=False, **_kwargs
):
makedirs(fspath_py35(to_info.parent), exist_ok=True)
makedirs(to_info.parent, exist_ok=True)
tmp_file = tmp_fname(to_info)
copyfile(
@@ -236,41 +236,16 @@ class RemoteLOCAL(RemoteBASE):
self, from_info, to_file, name=None, no_progress_bar=False, **_kwargs
):
copyfile(
fspath_py35(from_info),
to_file,
no_progress_bar=no_progress_bar,
name=name,
from_info, to_file, no_progress_bar=no_progress_bar, name=name
)
@staticmethod
def open(path_info, mode="r", encoding=None):
return open(fspath_py35(path_info), mode=mode, encoding=encoding)
def _group(self, checksum_infos, show_checksums=False):
by_md5 = {}
for info in checksum_infos:
md5 = info[self.PARAM_CHECKSUM]
if show_checksums:
by_md5[md5] = {"name": md5}
continue
name = info["name"]
branch = info.get("branch")
if branch:
name += "({})".format(branch)
if md5 not in by_md5.keys():
by_md5[md5] = {"name": name}
else:
by_md5[md5]["name"] += " " + name
return by_md5
def status(
self,
checksum_infos,
named_cache,
remote,
jobs=None,
show_checksums=False,
@@ -279,15 +254,14 @@ class RemoteLOCAL(RemoteBASE):
logger.debug(
"Preparing to collect status from {}".format(remote.path_info)
)
ret = self._group(checksum_infos, show_checksums=show_checksums) or {}
md5s = list(ret)
md5s = list(named_cache[self.scheme])
logger.debug("Collecting information from local cache...")
local_exists = self.cache_exists(md5s, jobs=jobs, name=self.cache_dir)
# This is a performance optimization. We can safely assume that,
# if the resources that we want to fetch are already cached,
# there's no need to check the remote storage for the existance of
# there's no need to check the remote storage for the existence of
# those files.
if download and sorted(local_exists) == sorted(md5s):
remote_exists = local_exists
@@ -299,6 +273,10 @@ class RemoteLOCAL(RemoteBASE):
)
)
ret = {
checksum: {"name": checksum if show_checksums else " ".join(names)}
for checksum, names in named_cache[self.scheme].items()
}
self._fill_statuses(ret, local_exists, remote_exists)
self._log_missing_caches(ret)
@@ -338,7 +316,7 @@ class RemoteLOCAL(RemoteBASE):
def _process(
self,
checksum_infos,
named_cache,
remote,
jobs=None,
show_checksums=False,
@@ -366,7 +344,7 @@ class RemoteLOCAL(RemoteBASE):
jobs = remote.JOBS
status_info = self.status(
checksum_infos,
named_cache,
remote,
jobs=jobs,
show_checksums=show_checksums,
@@ -379,7 +357,7 @@ class RemoteLOCAL(RemoteBASE):
return 0
if jobs > 1:
with TqdmThreadPoolExecutor(max_workers=jobs) as executor:
with ThreadPoolExecutor(max_workers=jobs) as executor:
fails = sum(executor.map(func, *plans))
else:
fails = sum(map(func, *plans))
@@ -391,18 +369,18 @@ class RemoteLOCAL(RemoteBASE):
return len(plans[0])
def push(self, checksum_infos, remote, jobs=None, show_checksums=False):
def push(self, named_cache, remote, jobs=None, show_checksums=False):
return self._process(
checksum_infos,
named_cache,
remote,
jobs=jobs,
show_checksums=show_checksums,
download=False,
)
def pull(self, checksum_infos, remote, jobs=None, show_checksums=False):
def pull(self, named_cache, remote, jobs=None, show_checksums=False):
return self._process(
checksum_infos,
named_cache,
remote,
jobs=jobs,
show_checksums=show_checksums,
+4 -5
View File
@@ -1,15 +1,14 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import os
import logging
from dvc.scheme import Schemes
import os
from dvc.config import Config
from dvc.remote.base import RemoteBASE
from dvc.progress import Tqdm
from dvc.path_info import CloudURLInfo
from dvc.progress import Tqdm
from dvc.remote.base import RemoteBASE
from dvc.scheme import Schemes
logger = logging.getLogger(__name__)
+1
View File
@@ -1,5 +1,6 @@
from collections import deque
from contextlib import contextmanager
from funcy import memoize
+58 -18
View File
@@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import logging
import os
from funcy import cached_property
from dvc.progress import Tqdm
from dvc.config import Config
from dvc.remote.base import RemoteBASE
from dvc.exceptions import DvcException, ETagMismatchError
from dvc.exceptions import DvcException
from dvc.exceptions import ETagMismatchError
from dvc.path_info import CloudURLInfo
from dvc.progress import Tqdm
from dvc.remote.base import RemoteBASE
from dvc.scheme import Schemes
logger = logging.getLogger(__name__)
@@ -36,7 +39,10 @@ class RemoteS3(RemoteBASE):
self.endpoint_url = config.get(Config.SECTION_AWS_ENDPOINT_URL)
self.list_objects = config.get(Config.SECTION_AWS_LIST_OBJECTS)
if config.get(Config.SECTION_AWS_LIST_OBJECTS):
self.list_objects_api = "list_objects"
else:
self.list_objects_api = "list_objects_v2"
self.use_ssl = config.get(Config.SECTION_AWS_USE_SSL, True)
@@ -139,13 +145,13 @@ class RemoteS3(RemoteBASE):
# multipart.
#
# If an object's etag looks like '8978c98bb5a48c2fb5f2c4c905768afa',
# then it was transfered as a single part, which means that the chunk
# then it was transferred as a single part, which means that the chunk
# size used to transfer it was greater or equal to the ContentLength
# of that object. So to preserve that tag over the next transfer, we
# could use any value >= ContentLength.
#
# If an object's etag looks like '50d67013a5e1a4070bef1fc8eea4d5f9-13',
# then it was transfered as a multipart, which means that the chunk
# then it was transferred as a multipart, which means that the chunk
# size used to transfer it was less than ContentLength of that object.
# Unfortunately, in general, it doesn't mean that the chunk size was
# the same throughout the transfer, so it means that in order to
@@ -180,27 +186,57 @@ class RemoteS3(RemoteBASE):
logger.debug("Removing {}".format(path_info))
self.s3.delete_object(Bucket=path_info.bucket, Key=path_info.path)
def _list_paths(self, bucket, prefix):
def _list_objects(self, path_info, max_items=None):
""" Read config for list object api, paginate through list objects."""
kwargs = {"Bucket": bucket, "Prefix": prefix}
if self.list_objects:
list_objects_api = "list_objects"
else:
list_objects_api = "list_objects_v2"
paginator = self.s3.get_paginator(list_objects_api)
kwargs = {
"Bucket": path_info.bucket,
"Prefix": path_info.path,
"PaginationConfig": {"MaxItems": max_items},
}
paginator = self.s3.get_paginator(self.list_objects_api)
for page in paginator.paginate(**kwargs):
contents = page.get("Contents", None)
if not contents:
continue
for item in contents:
yield item["Key"]
yield item
def _list_paths(self, path_info, max_items=None):
return (
item["Key"] for item in self._list_objects(path_info, max_items)
)
def list_cache_paths(self):
return self._list_paths(self.path_info.bucket, self.path_info.path)
return self._list_paths(self.path_info)
def exists(self, path_info):
paths = self._list_paths(path_info.bucket, path_info.path)
return any(path_info.path == path for path in paths)
dir_path = path_info / ""
fname = next(self._list_paths(path_info, max_items=1), "")
return path_info.path == fname or fname.startswith(dir_path.path)
def isdir(self, path_info):
# S3 doesn't have a concept for directories.
#
# Using `head_object` with a path pointing to a directory
# will throw a 404 error.
#
# A reliable way to know if a given path is a directory is by
# checking if there are more files sharing the same prefix
# with a `list_objects` call.
#
# We need to make sure that the path ends with a forward slash,
# since we can end with false-positives like the following example:
#
# bucket
# └── data
# ├── alice
# └── alpha
#
# Using `data/al` as prefix will return `[data/alice, data/alpha]`,
# While `data/al/` will return nothing.
#
dir_path = path_info / ""
return bool(list(self._list_paths(dir_path, max_items=1)))
def _upload(self, from_file, to_info, name=None, no_progress_bar=False):
total = os.path.getsize(from_file)
@@ -234,3 +270,7 @@ class RemoteS3(RemoteBASE):
return self.s3.generate_presigned_url(
ClientMethod="get_object", Params=params, ExpiresIn=int(expires)
)
def walk_files(self, path_info, max_items=None):
for fname in self._list_paths(path_info, max_items):
yield path_info.replace(path=fname)
+3 -2
View File
@@ -1,9 +1,10 @@
import logging
import time
import sys
import time
from functools import wraps
import colorama
from dvc.config import Config
@@ -18,7 +19,7 @@ this.message = (
"See {blue}https://dvc.org/doc/commands-reference/config#cache{reset} "
"for more information.\n"
"To disable this message, run:\n"
"'dvc config cache.slow_link_warning false'".format(
"`dvc config cache.slow_link_warning false`".format(
blue=colorama.Fore.BLUE, reset=colorama.Fore.RESET
)
)
+26 -17
View File
@@ -1,23 +1,25 @@
from __future__ import unicode_literals
import errno
import itertools
import io
import os
import getpass
import io
import itertools
import logging
import os
import threading
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager, closing
from contextlib import closing
from contextlib import contextmanager
import dvc.prompt as prompt
from dvc.config import Config
from dvc.utils import to_chunks
from dvc.utils.compat import urlparse, StringIO
from dvc.remote.base import RemoteBASE
from dvc.scheme import Schemes
from dvc.remote.pool import get_connection
from dvc.progress import Tqdm
from dvc.remote.base import RemoteBASE
from dvc.remote.pool import get_connection
from dvc.scheme import Schemes
from dvc.utils import to_chunks
from dvc.utils.compat import StringIO
from dvc.utils.compat import urlparse
logger = logging.getLogger(__name__)
@@ -115,9 +117,10 @@ class RemoteSSH(RemoteBASE):
return identity_file[0]
return None
def ssh(self, path_info):
def ensure_credentials(self, path_info=None):
if path_info is None:
path_info = self.path_info
host, user, port = path_info.host, path_info.user, path_info.port
# NOTE: we use the same password regardless of the server :(
if self.ask_password and self.password is None:
with saved_passwords_lock:
@@ -133,13 +136,16 @@ class RemoteSSH(RemoteBASE):
)
self.password = password
def ssh(self, path_info):
self.ensure_credentials(path_info)
from .connection import SSHConnection
return get_connection(
SSHConnection,
host,
username=user,
port=port,
path_info.host,
username=path_info.user,
port=path_info.port,
key_filename=self.keyfile,
timeout=self.timeout,
password=self.password,
@@ -260,10 +266,10 @@ class RemoteSSH(RemoteBASE):
with self.ssh(self.path_info) as ssh:
return list(ssh.walk_files(self.path_info.path))
def walk(self, path_info):
def walk_files(self, path_info):
with self.ssh(path_info) as ssh:
for entry in ssh.walk(path_info.path):
yield entry
for fname in ssh.walk_files(path_info.path):
yield path_info.replace(path=fname)
def makedirs(self, path_info):
with self.ssh(path_info) as ssh:
@@ -306,6 +312,9 @@ class RemoteSSH(RemoteBASE):
if not self.no_traverse:
return list(set(checksums) & set(self.all()))
# possibly prompt for credentials before "Querying" progress output
self.ensure_credentials()
with Tqdm(
desc="Querying "
+ ("cache in " + name if name else "remote cache"),
+22 -3
View File
@@ -1,8 +1,9 @@
import errno
import logging
import os
import posixpath
import logging
import errno
import stat
from funcy import cached_property
try:
@@ -185,8 +186,26 @@ class SSHConnection:
self.sftp.get(src, dest, callback=pbar.update_to)
def move(self, src, dst):
"""Rename src to dst, if it is not possible (in case src and dst are
on different filesystems) and actual physical copying of data is
happening.
"""
self.makedirs(posixpath.dirname(dst))
self.sftp.rename(src, dst)
try:
self.sftp.rename(src, dst)
except OSError:
self._atomic_copy(src, dst)
self.remove(src)
def _atomic_copy(self, src, dst):
tmp = tmp_fname(dst)
try:
self.copy(src, tmp)
self.sftp.rename(tmp, dst)
finally:
self.remove(tmp)
def upload(self, src, dest, no_progress_bar=False, progress_title=None):
self.makedirs(posixpath.dirname(dest))
+23 -26
View File
@@ -1,26 +1,28 @@
from __future__ import unicode_literals
import os
import logging
import os
from contextlib import contextmanager
from functools import wraps
from itertools import chain
from functools import wraps
from funcy import cached_property
from .graph import check_acyclic
from .graph import get_pipeline
from .graph import get_pipelines
from .graph import get_stages
from dvc.config import Config
from dvc.exceptions import (
NotDvcRepoError,
OutputNotFoundError,
FileMissingError,
)
from dvc.exceptions import FileMissingError
from dvc.exceptions import NotDvcRepoError
from dvc.exceptions import OutputNotFoundError
from dvc.ignore import DvcIgnoreFilter
from dvc.path_info import PathInfo
from dvc.remote.base import RemoteActionNotImplemented
from dvc.utils.compat import open as _open, fspath_py35, FileNotFoundError
from dvc.utils import relpath
from .graph import check_acyclic, get_pipeline, get_pipelines, get_stages
from dvc.utils.compat import FileNotFoundError
from dvc.utils.compat import fspath_py35
from dvc.utils.compat import open as _open
logger = logging.getLogger(__name__)
@@ -213,6 +215,7 @@ class Repo(object):
all_branches=False,
with_deps=False,
all_tags=False,
all_commits=False,
remote=None,
force=False,
jobs=None,
@@ -231,18 +234,14 @@ class Repo(object):
A dictionary with Schemes (representing output's location) as keys,
and a list with the outputs' `dumpd` as values.
"""
from dvc.cache import NamedCache
cache = {}
cache["local"] = []
cache["s3"] = []
cache["gs"] = []
cache["hdfs"] = []
cache["ssh"] = []
cache["azure"] = []
cache["repo"] = []
cache = NamedCache()
for branch in self.brancher(
all_branches=all_branches, all_tags=all_tags
all_branches=all_branches,
all_tags=all_tags,
all_commits=all_commits,
):
if targets:
stages = []
@@ -256,18 +255,16 @@ class Repo(object):
for stage in stages:
if stage.is_repo_import:
cache["repo"] += stage.deps
dep, = stage.deps
cache.external[dep.repo_pair].add(dep.def_path)
continue
for out in stage.outs:
scheme = out.path_info.scheme
used_cache = out.get_used_cache(
remote=remote, force=force, jobs=jobs
)
cache[scheme].extend(
dict(entry, branch=branch) for entry in used_cache
)
suffix = "({})".format(branch) if branch else ""
cache.update(used_cache, suffix=suffix)
return cache
@@ -499,7 +496,7 @@ class Repo(object):
@cached_property
def dvcignore(self):
return DvcIgnoreFilter(self.root_dir)
return DvcIgnoreFilter(self.root_dir, self.tree)
def close(self):
self.scm.close()
+47 -30
View File
@@ -1,54 +1,67 @@
from __future__ import unicode_literals
import os
import logging
import colorama
import os
from dvc.repo.scm_context import scm_context
from dvc.stage import Stage
from dvc.utils import walk_files, LARGE_DIR_SIZE
from dvc.exceptions import RecursiveAddingWhileUsingFilename
import colorama
from six import string_types
from . import locked
from dvc.exceptions import RecursiveAddingWhileUsingFilename
from dvc.progress import Tqdm
from dvc.repo.scm_context import scm_context
from dvc.stage import Stage
from dvc.utils import LARGE_DIR_SIZE
from dvc.utils import walk_files
logger = logging.getLogger(__name__)
@locked
@scm_context
def add(repo, target, recursive=False, no_commit=False, fname=None):
def add(repo, targets, recursive=False, no_commit=False, fname=None):
if recursive and fname:
raise RecursiveAddingWhileUsingFilename()
targets = _find_all_targets(repo, target, recursive)
if isinstance(targets, string_types):
targets = [targets]
if os.path.isdir(target) and len(targets) > LARGE_DIR_SIZE:
logger.warning(
"You are adding a large directory '{target}' recursively,"
" consider tracking it as a whole instead.\n"
"{purple}HINT:{nc} Remove the generated DVC-file and then"
" run {cyan}dvc add {target}{nc}".format(
purple=colorama.Fore.MAGENTA,
cyan=colorama.Fore.CYAN,
nc=colorama.Style.RESET_ALL,
target=target,
)
)
stages_list = []
with Tqdm(total=len(targets), desc="Add", unit="file", leave=True) as pbar:
for target in targets:
sub_targets = _find_all_targets(repo, target, recursive)
pbar.total += len(sub_targets) - 1
stages = _create_stages(repo, targets, fname)
if os.path.isdir(target) and len(sub_targets) > LARGE_DIR_SIZE:
logger.warning(
"You are adding a large directory '{target}' recursively,"
" consider tracking it as a whole instead.\n"
"{purple}HINT:{nc} Remove the generated DVC-file and then"
" run {cyan}dvc add {target}{nc}".format(
purple=colorama.Fore.MAGENTA,
cyan=colorama.Fore.CYAN,
nc=colorama.Style.RESET_ALL,
target=target,
)
)
repo.check_modified_graph(stages)
stages = _create_stages(repo, sub_targets, fname, pbar=pbar)
for stage in stages:
stage.save()
repo.check_modified_graph(stages)
if not no_commit:
stage.commit()
for stage in stages:
stage.save()
stage.dump()
if not no_commit:
stage.commit()
return stages
stage.dump()
stages_list += stages
# remove filled bar bit of progress, leaving stats
pbar.bar_format = pbar.BAR_FMT_DEFAULT.replace("|{bar:10}|", " ")
return stages_list
def _find_all_targets(repo, target, recursive):
@@ -64,15 +77,19 @@ def _find_all_targets(repo, target, recursive):
return [target]
def _create_stages(repo, targets, fname):
def _create_stages(repo, targets, fname, pbar=None):
stages = []
for out in targets:
stage = Stage.create(repo, outs=[out], add=True, fname=fname)
if not stage:
if pbar is not None:
pbar.total -= 1
continue
stages.append(stage)
if pbar is not None:
pbar.update_desc(out)
return stages
+23 -13
View File
@@ -1,8 +1,15 @@
from funcy import group_by
from dvc.scm.tree import WorkingTree
def brancher( # noqa: E302
self, branches=None, all_branches=False, tags=None, all_tags=False
self,
branches=None,
all_branches=False,
tags=None,
all_tags=False,
all_commits=False,
):
"""Generator that iterates over specified revisions.
@@ -18,7 +25,7 @@ def brancher( # noqa: E302
- empty string it there is no branches to iterate over
- "Working Tree" if there are uncommited changes in the SCM repo
"""
if not any([branches, all_branches, tags, all_tags]):
if not any([branches, all_branches, tags, all_tags, all_commits]):
yield ""
return
@@ -30,25 +37,28 @@ def brancher( # noqa: E302
self.tree = WorkingTree(self.root_dir)
yield "working tree"
if all_branches:
branches = scm.list_branches()
if all_commits:
revs = scm.list_all_commits()
else:
if all_branches:
branches = scm.list_branches()
if all_tags:
tags = scm.list_tags()
if all_tags:
tags = scm.list_tags()
if branches is not None:
revs.extend(branches)
if branches is not None:
revs.extend(branches)
if tags is not None:
revs.extend(tags)
if tags is not None:
revs.extend(tags)
# NOTE: it might be a good idea to wrap this loop in try/finally block
# to don't leave the tree on some unexpected branch after the
# `brancher()`, but this could cause problems on exception handling
# code which might expect the tree on which exception was raised to
# stay in place. This behavior is a subject to change.
for rev in revs:
self.tree = scm.get_tree(rev)
yield rev
for sha, names in group_by(scm.resolve_rev, revs).items():
self.tree = scm.get_tree(sha)
yield ", ".join(names)
self.tree = saved_tree
+22 -18
View File
@@ -2,21 +2,22 @@ from __future__ import unicode_literals
import logging
from dvc.exceptions import CheckoutErrorSuggestGit, CheckoutError
from dvc.exceptions import CheckoutError
from dvc.exceptions import CheckoutErrorSuggestGit
from dvc.progress import Tqdm
logger = logging.getLogger(__name__)
def _cleanup_unused_links(self, all_stages):
def _cleanup_unused_links(repo):
used = [
out.fspath
for stage in all_stages
for stage in repo.stages
for out in stage.outs
if out.scheme == "local"
]
self.state.remove_unused_links(used)
repo.state.remove_unused_links(used)
def get_all_files_numbers(stages):
@@ -24,18 +25,27 @@ def get_all_files_numbers(stages):
def _checkout(
self, target=None, with_deps=False, force=False, recursive=False
self, targets=None, with_deps=False, force=False, recursive=False
):
from dvc.stage import StageFileDoesNotExistError, StageFileBadNameError
try:
stages = self.collect(target, with_deps=with_deps, recursive=recursive)
except (StageFileDoesNotExistError, StageFileBadNameError) as exc:
if not target:
raise
raise CheckoutErrorSuggestGit(target, exc)
stages = set()
if not targets:
targets = [None]
_cleanup_unused_links(self)
for target in targets:
try:
new = self.collect(
target, with_deps=with_deps, recursive=recursive
)
stages.update(new)
except (StageFileDoesNotExistError, StageFileBadNameError) as exc:
if not target:
raise
raise CheckoutErrorSuggestGit(target, exc)
_cleanup_unused_links(self, self.stages)
total = get_all_files_numbers(stages)
if total == 0:
logger.info("Nothing to do")
@@ -44,12 +54,6 @@ def _checkout(
total=total, unit="file", desc="Checkout", disable=total == 0
) as pbar:
for stage in stages:
if stage.locked:
logger.warning(
"DVC-file '{path}' is locked. Its dependencies are"
" not going to be checked out.".format(path=stage.relpath)
)
failed.extend(
stage.checkout(force=force, progress_callback=pbar.update_desc)
)
+6 -10
View File
@@ -3,18 +3,14 @@ from __future__ import unicode_literals
import os
from errno import ENOENT
import dvc.logger as logger
from dvc.scm.base import FileNotInCommitError
from dvc.scm.git import (
DIFF_A_TREE,
DIFF_B_TREE,
DIFF_A_REF,
DIFF_B_REF,
DIFF_EQUAL,
)
from . import locked
from dvc.scm.base import FileNotInCommitError
from dvc.scm.git import DIFF_A_REF
from dvc.scm.git import DIFF_A_TREE
from dvc.scm.git import DIFF_B_REF
from dvc.scm.git import DIFF_B_TREE
from dvc.scm.git import DIFF_EQUAL
DIFF_TARGET = "target"
+44 -16
View File
@@ -2,8 +2,11 @@ from __future__ import unicode_literals
import logging
from dvc.cache import NamedCache
from dvc.config import NoRemoteError
from dvc.exceptions import DownloadError, OutputNotFoundError
from dvc.exceptions import DownloadError
from dvc.exceptions import OutputNotFoundError
from dvc.external_repo import external_repo
from dvc.scm.base import CloneError
@@ -24,7 +27,7 @@ def _fetch(
"""Download data items from a cloud and imported repositories
Returns:
int: number of succesfully downloaded files
int: number of successfully downloaded files
Raises:
DownloadError: thrown when there are failed downloads, either
@@ -49,28 +52,53 @@ def _fetch(
try:
downloaded += self.cloud.pull(
used["local"], jobs, remote=remote, show_checksums=show_checksums
used, jobs, remote=remote, show_checksums=show_checksums
)
except NoRemoteError:
if not used["repo"] and used["local"]:
if not used.external and used["local"]:
raise
except DownloadError as exc:
failed += exc.amount
for dep in used["repo"]:
try:
out = dep.fetch()
downloaded += out.get_files_number()
except DownloadError as exc:
failed += exc.amount
except (CloneError, OutputNotFoundError):
failed += 1
logger.exception(
"failed to fetch data for '{}'".format(dep.stage.outs[0])
)
for (repo_url, repo_rev), files in used.external.items():
d, f = _fetch_external(self, repo_url, repo_rev, files)
downloaded += d
failed += f
if failed:
raise DownloadError(failed)
return downloaded
def _fetch_external(self, repo_url, repo_rev, files):
failed = 0
cache_dir = self.cache.local.cache_dir
try:
with external_repo(repo_url, repo_rev, cache_dir=cache_dir) as repo:
with repo.state:
cache = NamedCache()
for name in files:
try:
out = repo.find_out_by_relpath(name)
except OutputNotFoundError:
failed += 1
logger.exception(
"failed to fetch data for '{}'".format(name)
)
continue
else:
cache.update(out.get_used_cache())
try:
return repo.cloud.pull(cache), failed
except DownloadError as exc:
failed += exc.amount
except CloneError:
failed += 1
logger.exception(
"failed to fetch data for '{}'".format(", ".join(files))
)
return 0, failed
+22 -61
View File
@@ -1,53 +1,14 @@
from __future__ import unicode_literals
import collections
import logging
from . import locked
from dvc.cache import NamedCache
logger = logging.getLogger(__name__)
def _merge_cache_lists(clists):
merged_cache = collections.defaultdict(list)
for cache_list in clists:
for scheme, cache in cache_list.items():
for item in cache:
if item not in merged_cache[scheme]:
merged_cache[scheme].append(item)
return merged_cache
def _load_all_used_cache(
repos,
all_branches=False,
with_deps=False,
all_tags=False,
remote=None,
force=False,
jobs=None,
):
clists = []
for repo in repos:
repo_clist = repo.used_cache(
targets=None,
all_branches=all_branches,
with_deps=with_deps,
all_tags=all_tags,
remote=remote,
force=force,
jobs=jobs,
)
clists.append(repo_clist)
return clists
def _do_gc(typ, func, clist):
removed = func(clist)
if not removed:
@@ -62,6 +23,7 @@ def gc(
remote=None,
with_deps=False,
all_tags=False,
all_commits=False,
force=False,
jobs=None,
repos=None,
@@ -79,37 +41,36 @@ def gc(
stack.enter_context(repo.lock)
stack.enter_context(repo.state)
all_clists = _load_all_used_cache(
all_repos + [self],
all_branches=all_branches,
with_deps=with_deps,
all_tags=all_tags,
remote=remote,
force=force,
jobs=jobs,
)
used = NamedCache()
for repo in all_repos + [self]:
used.update(
repo.used_cache(
all_branches=all_branches,
with_deps=with_deps,
all_tags=all_tags,
all_commits=all_commits,
remote=remote,
force=force,
jobs=jobs,
)
)
if len(all_clists) > 1:
clist = _merge_cache_lists(all_clists)
else:
clist = all_clists[0]
_do_gc("local", self.cache.local.gc, clist)
_do_gc("local", self.cache.local.gc, used)
if self.cache.s3:
_do_gc("s3", self.cache.s3.gc, clist)
_do_gc("s3", self.cache.s3.gc, used)
if self.cache.gs:
_do_gc("gs", self.cache.gs.gc, clist)
_do_gc("gs", self.cache.gs.gc, used)
if self.cache.ssh:
_do_gc("ssh", self.cache.ssh.gc, clist)
_do_gc("ssh", self.cache.ssh.gc, used)
if self.cache.hdfs:
_do_gc("hdfs", self.cache.hdfs.gc, clist)
_do_gc("hdfs", self.cache.hdfs.gc, used)
if self.cache.azure:
_do_gc("azure", self.cache.azure.gc, clist)
_do_gc("azure", self.cache.azure.gc, used)
if cloud:
_do_gc("remote", self.cloud.get_remote(remote, "gc -c").gc, clist)
_do_gc("remote", self.cloud.get_remote(remote, "gc -c").gc, used)
+10 -15
View File
@@ -1,19 +1,18 @@
import logging
import os
import shortuuid
from dvc.stage import Stage
from dvc.config import Config
from dvc.exceptions import (
NotDvcRepoError,
UrlNotDvcRepoError,
GetDVCFileError,
OutputNotFoundError,
)
from dvc.path_info import PathInfo
from dvc.exceptions import GetDVCFileError
from dvc.exceptions import NotDvcRepoError
from dvc.exceptions import OutputNotFoundError
from dvc.exceptions import UrlNotDvcRepoError
from dvc.external_repo import external_repo
from dvc.path_info import PathInfo
from dvc.stage import Stage
from dvc.state import StateNoop
from dvc.utils import remove, resolve_output
from dvc.utils import remove
from dvc.utils import resolve_output
logger = logging.getLogger(__name__)
@@ -48,11 +47,7 @@ def get(url, path, out=None, rev=None):
#
# Also, we can't use theoretical "move" link type here, because
# the same cache file might be used a few times in a directory.
repo.config.set(
Config.SECTION_CACHE,
Config.SECTION_CACHE_TYPE,
"reflink,hardlink,copy",
)
repo.cache.local.cache_types = ["reflink", "hardlink", "copy"]
o = repo.find_out_by_relpath(path)
with repo.state:
+3 -3
View File
@@ -1,8 +1,7 @@
import os
import dvc.output as output
import dvc.dependency as dependency
import dvc.output as output
from dvc.utils import resolve_output
@@ -12,7 +11,8 @@ def get_url(url, out=None):
if os.path.exists(url):
url = os.path.abspath(url)
out = os.path.abspath(out)
out = os.path.abspath(out)
dep, = dependency.loads_from(None, [url])
out, = output.loads_from(None, [out], use_cache=False)
+10 -3
View File
@@ -5,10 +5,17 @@ def check_acyclic(graph):
import networkx as nx
from dvc.exceptions import CyclicGraphError
cycle = next(nx.simple_cycles(graph), None)
try:
edges = nx.find_cycle(graph, orientation="original")
except nx.NetworkXNoCycle:
return
if cycle:
raise CyclicGraphError(cycle)
stages = set()
for from_node, to_node, _ in edges:
stages.add(from_node)
stages.add(to_node)
raise CyclicGraphError(list(stages))
def get_pipeline(pipelines, node):
+2 -3
View File
@@ -1,7 +1,6 @@
from dvc.utils import resolve_output
from dvc.repo.scm_context import scm_context
from . import locked as locked_repo
from dvc.repo.scm_context import scm_context
from dvc.utils import resolve_output
@locked_repo
+12 -8
View File
@@ -1,13 +1,17 @@
import os
import colorama
import logging
import os
from dvc.repo import Repo
from dvc.scm import SCM, NoSCM
import colorama
from dvc.analytics import Analytics
from dvc.config import Config
from dvc.exceptions import InitError
from dvc.utils import boxify, relpath, remove
from dvc.analytics import Analytics
from dvc.repo import Repo
from dvc.scm import NoSCM
from dvc.scm import SCM
from dvc.utils import boxify
from dvc.utils import relpath
from dvc.utils import remove
logger = logging.getLogger(__name__)
@@ -65,7 +69,7 @@ def init(root_dir=os.curdir, no_scm=False, force=False):
if isinstance(scm, NoSCM) and not no_scm:
raise InitError(
"{repo} is not tracked by any supported scm tool (e.g. git). "
"Use '--no-scm' if you don't want to use any scm.".format(
"Use `--no-scm` if you don't want to use any scm.".format(
repo=root_dir
)
)
@@ -73,7 +77,7 @@ def init(root_dir=os.curdir, no_scm=False, force=False):
if os.path.isdir(dvc_dir):
if not force:
raise InitError(
"'{repo}' exists. Use '-f' to force.".format(
"'{repo}' exists. Use `-f` to force.".format(
repo=relpath(dvc_dir)
)
)
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from dvc.repo import locked
from dvc.exceptions import DvcException
from dvc.repo import locked
@locked
+8 -4
View File
@@ -1,16 +1,20 @@
from __future__ import unicode_literals
import errno
import os
import csv
import errno
import json
import logging
import os
from jsonpath_ng.ext import parse
from dvc.exceptions import NoMetricsError
from dvc.exceptions import OutputNotFoundError
from dvc.repo import locked
from dvc.exceptions import OutputNotFoundError, NoMetricsError
from dvc.utils.compat import builtin_str, open, StringIO, csv_reader
from dvc.utils.compat import builtin_str
from dvc.utils.compat import csv_reader
from dvc.utils.compat import open
from dvc.utils.compat import StringIO
NO_METRICS_FILE_AT_REFERENCE_WARNING = (
"Metrics file '{}' does not exist at the reference '{}'."

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