Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BF: revert back (remove) check for path being PathRI #3425

Merged
merged 3 commits into from May 19, 2019

Conversation

@yarikoptic
Copy link
Member

commented May 16, 2019

Paths with : in them could get confused for SSHRI. If we know that it
is a path, why to check/crash? Original commit
86a05d0
which introduced that check unfortunately does not provide ANY information
on the initial problem it addresses, besides hinting possibly on it being related
to Windows

Closes #3421

BF: revert back (remove) check for path being PathRI
Paths with : in them could get confused for SSHRI.  If we know that it
is a path, why to check/crash?  Original commit
86a05d0
which introduced that check unfortunately does not provide ANY information
on the initial problem it addresses, besides hinting possibly on it being related
to Windows
@mih

This comment has been minimized.

Copy link
Member

commented May 16, 2019

Original reason rediscovered:

======================================================================
1346ERROR: datalad.distribution.tests.test_clone.test_invalid_args
1347----------------------------------------------------------------------
1348Traceback (most recent call last):
....
1387datalad.support.exceptions.IncompleteResultsError: Command did not complete successfully [{'action': 'install', 'message': ('Failed to clone from any candidate source URL. Encountered errors per each url were: %s', (OrderedDict([('Zoidberg', "Cmd('git') failed due to: exit code(128)\n  cmdline: git clone --progress -v Zoidberg C:\\projects\\datalad\\__testhome__\\ssh:\\mars:Zoidberg [cmd.py:wait:415]")]),)), 'source_url': 'Zoidberg', 'status': 'error', 'path': 'C:\\projects\\datalad\\__testhome__\\ssh:\\mars:Zoidberg', 'type': 'dataset'}]

Possibly related to #2574 (comment) (which popped up in other issue too, but I cannot find them now)

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 16, 2019

yeah, deeper issue is RI resolution. looking into that ATM.

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 16, 2019

SSHRI issue is fixed, but I think the issue is twofold - us not checking "higher" for a correct type of the RI... will get to that when get to work

@codecov

This comment has been minimized.

Copy link

commented May 16, 2019

Codecov Report

Merging #3425 into 0.11.x will increase coverage by 0.02%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           0.11.x    #3425      +/-   ##
==========================================
+ Coverage      91%   91.02%   +0.02%     
==========================================
  Files         255      255              
  Lines       33442    33450       +8     
==========================================
+ Hits        30433    30448      +15     
+ Misses       3009     3002       -7
Impacted Files Coverage Δ
datalad/distribution/dataset.py 94.14% <ø> (-0.08%) ⬇️
datalad/interface/tests/test_save.py 100% <100%> (ø) ⬆️
datalad/support/network.py 85.97% <100%> (ø) ⬆️
datalad/distribution/tests/test_clone.py 99.52% <100%> (ø) ⬆️
datalad/support/tests/test_network.py 100% <100%> (ø) ⬆️
datalad/downloaders/http.py 85.31% <0%> (+2.77%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 05bd760...60bfefe. Read the comment docs.

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 16, 2019

ok, fix for SSHRI is in 98a057e
and the fix for failing test is fixing the test, see rationale in the diff and commit message of f636b04

"""
_check_ri('example.com/path/sp1:fname', SSHRI, hostname='example.com/path/sp1', path='fname')
# _check_ri('example.com/path/sp1:fname', SSHRI, hostname='example.com/path/sp1', path='fname')

This comment has been minimized.

Copy link
@yarikoptic

yarikoptic May 16, 2019

Author Member

actually I will adjust here to check explicitly for being a PathRI, and bring back empty line above...

@yarikoptic yarikoptic force-pushed the yarikoptic:bf-3421 branch from b672d4f to dc57e37 May 16, 2019

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 16, 2019

oh hoh -- was supposed to go against 0.11.x ... travis/appveyor would get confused... so one more amend after changing the base.

@yarikoptic yarikoptic changed the base branch from master to 0.11.x May 16, 2019

@yarikoptic yarikoptic force-pushed the yarikoptic:bf-3421 branch from dc57e37 to f636b04 May 16, 2019

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 16, 2019

grr... fixed up even more, but started to wonder why I didn't generally do matching based on regex alone to decide, or at least as the 2nd check when making a decision. Oh well, may be check more on that later. Let's see how this one withstands testing

@kyleam
Copy link
Member

left a comment

Looking at each commit on its own, I think I can make sense of the local change, but, at least with the initial review of the commits, I'm struggling with the bigger picture of how these commits relate to each other and the Windows failure that's observed when only b94fc33 is applied to revert the overeager PathRI check in resolve_path().

Since you have it fresh in your mind, could you provide a summary?

datalad/support/network.py Show resolved Hide resolved
@@ -342,7 +342,12 @@ def _guess_ri_cls(ri):

if not fields['scheme'] and not fields['hostname']:
parts = _split_colon(ri)
if fields['path'] and '@' in fields['path'] or len(parts) > 1:
# if no illegal for hostname/username characters in the first part and

This comment has been minimized.

Copy link
@kyleam

kyleam May 17, 2019

Member

and...? Incomplete comment?

This comment has been minimized.

Copy link
@yarikoptic

yarikoptic May 17, 2019

Author Member

thanks -- will tune up now

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 17, 2019

Sorry I have not adjusted description from the original one.

Since you have it fresh in your mind, could you provide a summary?

"fresh" is already a stretch but indeed I still remember something ;)

Re "Windows failure" -- That windows failure had nothing Windows specific and was present also in Travis/locally: it was an assumption of the test that we will throw ValueError when providing a URL or URL-like where PATH is expected, instead of just returning the result record stating that clone has failed (upon git trying to clone, which it would happily do into URL or URL-like path). The corresponding change is https://github.com/datalad/datalad/pull/3425/files#diff-41a7499eba398003a269d03345930e78L69 in commit f636b04 which fixed that test.

Overall summary:

  1. stop guessing type of the RI whenever we expect a specific one, e.g. PathRI. "URL", "URL-like" etc could all be "legit" paths and we should not second guess "if it is a legit path" by checking if it could be anything else while annotating what should already be paths.
  2. fix up detection of SSHRI by relying on it having : (unless there is no path to follow) and no illegal for hostname characters (/\#) in the initial (username@hostname) part.

Makes sense?

yarikoptic added some commits May 16, 2019

BF(TST): do not assume user naiveness - treat any url-like looking pa…
…th as a path

clone  help/doc says that PATH must be a path.  Both ssh://X/Y and ssh://X:y
are valid (although I would not recommend them, and 2nd / would be
ignored) paths, so users should be able to clone into them.  Git seems
to perform that way as well:

    $> git clone git://github.com/ReproNim/reproseed https://github.com/ReproNim/reproseed
    Cloning into https://github.com/ReproNim/reproseed...
    remote: Enumerating objects: 44, done.
    remote: Counting objects: 100% (44/44), done.
    remote: Compressing objects: 100% (30/30), done.
    remote: Total 44 (delta 15), reused 35 (delta 11), pack-reused 0
    Receiving objects: 100% (44/44), 8.29 KiB | 8.29 MiB/s, done.
    Resolving deltas: 100% (15/15), done.

although does fails if both URLs match up (what we do too).

@yarikoptic yarikoptic force-pushed the yarikoptic:bf-3421 branch from 6831aa7 to 60bfefe May 17, 2019

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 17, 2019

force pushed after addressing comments, extending regex for sshri also to include # (to make it more consistent with the detection code), squashing two related commits

@kyleam

This comment has been minimized.

Copy link
Member

commented May 17, 2019

@kyleam

This comment has been minimized.

Copy link
Member

commented May 17, 2019

force pushed after addressing comments, extending regex for sshri also to include # (to make it more consistent with the detection code), squashing two related commits

Thanks!

range-diff
1:  98a057e41 ! 1:  2e5e9b1f7 BF: Forbid having / or \ in the hostname of the SSHRI while detecting
    @@ -1,6 +1,6 @@
     Author: Yaroslav Halchenko <debian@onerussian.com>
     
    -    BF: Forbid having / or \ in the hostname of the SSHRI while detecting
    +    BF: Check for /, \ or # in the username@hostname part while detecting SSHRI
     
         ref: see https://github.com/datalad/datalad/issues/3421
     
    @@ -12,8 +12,13 @@
          if not fields['scheme'] and not fields['hostname']:
              parts = _split_colon(ri)
     -        if fields['path'] and '@' in fields['path'] or len(parts) > 1:
    -+        if fields['path'] and '@' in fields['path'] \
    -+                or (len(parts) > 1 and not set(parts[0]).intersection(set('/\\#'))):
    ++        # if no illegal for username@hostname characters in the first part and
    ++        # we either had username@hostname or multiple :-separated parts
    ++        if not set(parts[0]).intersection(set('/\\#')) and (
    ++                fields['path'] and
    ++                '@' in fields['path'] or
    ++                len(parts) > 1
    ++        ):
                  # user@host:path/sp1
                  # or host_name: (hence parts check)
                  # TODO: we need a regex to catch those really, parts check is not suff
    @@ -22,7 +27,7 @@
          )
      
     -    _REGEX = re.compile(r'((?P<username>\S*)@)?(?P<hostname>[^:]+)(\:(?P<path>.*))?$')
    -+    _REGEX = re.compile(r'((?P<username>\S*)@)?(?P<hostname>[^/\\:]+)(\:(?P<path>.*))?$')
    ++    _REGEX = re.compile(r'((?P<username>\S*)@)?(?P<hostname>[^#/\\:]+)(\:(?P<path>.*))?$')
      
          @classmethod
          def _normalize_fields(cls, fields):
    @@ -52,6 +57,7 @@
          """
     -    _check_ri('example.com/path/sp1:fname', SSHRI, hostname='example.com/path/sp1', path='fname')
     +    _check_ri('e.com/p/sp:f', PathRI, localpath='e.com/p/sp:f', path='e.com/p/sp:f')
    ++    _check_ri('user@someho.st/mydir', PathRI, localpath='user@someho.st/mydir', path='user@someho.st/mydir')
      
          # SSHRIs have .port, but it is empty
          eq_(SSHRI(hostname='example.com').port, '')
2:  f636b04d3 = 2:  60bfefe87 BF(TST): do not assume user naiveness - treat any url-like looking path as a path
3:  6831aa7de < -:  --------- BF: generally check for no inappropriate characters in the ssh RI beginning
@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 17, 2019

hm, the only failing run is for -neuroimaging:

$ http_proxy= PATH=$PWD/../tools/coverage-bin:$PATH $NOSE_WRAPPER `which nosetests` $NOSE_OPTS -A "$NOSE_SELECTION_OP($NOSE_SELECTION)" --with-doctest --with-cov --cover-package datalad --logging-level=INFO $TESTS_TO_PERFORM
.........SS.E.S.
======================================================================
ERROR: datalad_neuroimaging.tests.test_procedure.test_bids_procedure
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/virtualenv/python3.5.6/lib/python3.5/site-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/travis/virtualenv/python3.5.6/lib/python3.5/site-packages/datalad/tests/utils.py", line 615, in newfunc
    return t(*(arg + (filename,)), **kw)
  File "/home/travis/virtualenv/python3.5.6/lib/python3.5/site-packages/datalad_neuroimaging/tests/test_procedure.py", line 13, in test_bids_procedure
    ds.run_procedure(['cfg_bids'])
  File "/home/travis/virtualenv/python3.5.6/lib/python3.5/site-packages/datalad/distribution/dataset.py", line 526, in apply_func
    return f(**kwargs)
  File "/home/travis/virtualenv/python3.5.6/lib/python3.5/site-packages/datalad/interface/utils.py", line 492, in eval_func
    return return_func(generator_func)(*args, **kwargs)
  File "/home/travis/virtualenv/python3.5.6/lib/python3.5/site-packages/datalad/interface/utils.py", line 480, in return_func
    results = list(results)
  File "/home/travis/virtualenv/python3.5.6/lib/python3.5/site-packages/datalad/interface/utils.py", line 468, in generator_func
    msg="Command did not complete successfully")
datalad.support.exceptions.IncompleteResultsError: Command did not complete successfully [{'action': 'run_procedure', 'status': 'impossible', 'refds': '/tmp/datalad_temp_test_bids_procedure0ucvyzgv', 'path': 'cfg_bids', 'message': "Cannot find procedure with name 'cfg_bids'"}]
@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 17, 2019

I have tried to reproduce locally by running tests of datalad-neuroimaging -- do not see anything suspicious. and happens also for other PRs such as #3430 .

So unrelated to this one. Unless more objections are heard, will merge tomorrow morning

@kyleam

This comment has been minimized.

Copy link
Member

commented May 17, 2019

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 17, 2019

Oh, indeed. I i just saw that Travis also had a red cross, and assumed that the failure was the same... But I will check how that clone test managed to pass in that state when I get to the laptop.

@yarikoptic

This comment has been minimized.

Copy link
Member Author

commented May 18, 2019

ha! the test was not failing on Linux because ValueError was raised just at a different point but similarly (RI resolution to SSHRI) inflicted in a different spot:

$> python -m nose --pdb -s -v datalad/distribution/tests/test_clone.py:test_invalid_args
datalad.distribution.tests.test_clone.test_invalid_args ... > /home/yoh/proj/datalad/datalad/datalad/support/network.py(476)localpath()
-> raise ValueError("%s points to remote location" % self)
(Pdb) p self
SSHRI(hostname='/home/yoh/proj/datalad/datalad/ssh', path='/mars:Zoidberg')
(Pdb) bt
  /usr/lib/python2.7/unittest/case.py(329)run()
-> testMethod()
  /usr/lib/python2.7/dist-packages/nose/case.py(197)runTest()
-> self.test(*self.arg)
  /home/yoh/proj/datalad/datalad/datalad/tests/utils.py(615)newfunc()
-> return t(*(arg + (filename,)), **kw)
  /home/yoh/proj/datalad/datalad/datalad/tests/utils.py(615)newfunc()
-> return t(*(arg + (filename,)), **kw)
  /home/yoh/proj/datalad/datalad/datalad/tests/utils.py(615)newfunc()
-> return t(*(arg + (filename,)), **kw)
  /home/yoh/proj/datalad/datalad/datalad/distribution/tests/test_clone.py(70)test_invalid_args()
-> clone('Zoidberg', path='ssh://mars:Zoidberg')
  /home/yoh/proj/datalad/datalad/datalad/interface/utils.py(492)eval_func()
-> return return_func(generator_func)(*args, **kwargs)
  /home/yoh/proj/datalad/datalad/datalad/interface/utils.py(480)return_func()
-> results = list(results)
  /home/yoh/proj/datalad/datalad/datalad/interface/utils.py(429)generator_func()
-> result_renderer, result_xfm, _result_filter, **_kwargs):
  /home/yoh/proj/datalad/datalad/datalad/interface/utils.py(522)_process_results()
-> for res in results:
  /home/yoh/proj/datalad/datalad/datalad/distribution/clone.py(182)__call__()
-> destination_dataset = Dataset(path)
  /home/yoh/proj/datalad/datalad/datalad/support/repo.py(140)__call__()
-> id_, new_args, new_kwargs = cls._flyweight_id_from_args(*args, **kwargs)
  /home/yoh/proj/datalad/datalad/datalad/distribution/dataset.py(140)_flyweight_id_from_args()
-> path_ = RI(path_).localpath
  /home/yoh/proj/datalad/datalad/datalad/support/network.py(541)__getattribute__()
-> return super(RI, self).__getattribute__(item)
> /home/yoh/proj/datalad/datalad/datalad/support/network.py(476)localpath()
-> raise ValueError("%s points to remote location" % self)

So fixing the SSHRI parsing made it no longer fail here since it was no longer identified as an SSHRI.

@kyleam

This comment has been minimized.

Copy link
Member

commented May 18, 2019

@yarikoptic yarikoptic merged commit af4ae3b into datalad:0.11.x May 19, 2019

2 of 3 checks passed

continuous-integration/travis-ci/pr The Travis CI build failed
Details
WIP Legacy commit status override — see details
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details

@yarikoptic yarikoptic deleted the yarikoptic:bf-3421 branch May 20, 2019

yarikoptic added a commit that referenced this pull request May 28, 2019

Merge tag '0.11.5' into debian
0.11.5 (May 23, 2019) -- stability is not overrated

Should be faster and less buggy, with a few enhancements.

 Fixes

- [create-sibling][]  ([#3318][])
  - Siblings are no longer configured with a post-update hook unless a
    web interface is requested with `--ui`.
  - `git submodule update --init` is no longer called from the
    post-update hook.
  - If `--inherit` is given for a dataset without a superdataset, a
    warning is now given instead of raising an error.
- The internal command runner failed on Python 2 when its `env`
  argument had unicode values.  ([#3332][])
- The safeguard that prevents creating a dataset in a subdirectory
  that already contains tracked files for another repository failed on
  Git versions before 2.14.  For older Git versions, we now warn the
  caller that the safeguard is not active.  ([#3347][])
- A regression introduced in v0.11.1 prevented [save][] from committing
  changes under a subdirectory when the subdirectory was specified as
  a path argument.  ([#3106][])
- A workaround introduced in v0.11.1 made it possible for [save][] to
  do a partial commit with an annex file that has gone below the
  `annex.largefiles` threshold.  The logic of this workaround was
  faulty, leading to files being displayed as typechanged in the index
  following the commit.  ([#3365][])
- The resolve_path() helper confused paths that had a semicolon for
  SSH RIs.  ([#3425][])
- The detection of SSH RIs has been improved.  ([#3425][])

 Enhancements and new features

- The internal command runner was too aggressive in its decision to
  sleep.  ([#3322][])
- The "INFO" label in log messages now retains the default text color
  for the terminal rather than using white, which only worked well for
  terminals with dark backgrounds.  ([#3334][])
- A short flag `-R` is now available for the `--recursion-limit` flag,
  a flag shared by several subcommands.  ([#3340][])
- The authentication logic for [create-sibling-github][] has been
  revamped and now supports 2FA.  ([#3180][])
- New configuration option `datalad.ui.progressbar` can be used to
  configure the default backend for progress reporting ("none", for
  example, results in no progress bars being shown).  ([#3396][])
- A new progress backend, available by setting datalad.ui.progressbar
  to "log", replaces progress bars with a log message upon completion
  of an action.  ([#3396][])
- DataLad learned to consult the [NO_COLOR][] environment variable and
  the new `datalad.ui.color` configuration option when deciding to
  color output.  The default value, "auto", retains the current
  behavior of coloring output if attached to a TTY ([#3407][]).
- [clean][] now removes annex transfer directories, which is useful
  for cleaning up failed downloads. ([#3374][])
- [clone][] no longer refuses to clone into a local path that looks
  like a URL, making its behavior consistent with `git clone`.
  ([#3425][])
- [wtf][]
  - Learned to fall back to the `dist` package if `platform.dist`,
    which has been removed in the yet-to-be-release Python 3.8, does
    not exist.  ([#3439][])
  - Gained a `--section` option for limiting the output to specific
    sections and a `--decor` option, which currently knows how to
    format the output as GitHub's `<details>` section.  ([#3440][])

* tag '0.11.5': (96 commits)
  [DATALAD RUNCMD] make update-changelog
  Version boost and finalize CHANGELOG.md record
  ENH: new Makefile rule linkissues-changelog to link issues, which now will also be prerequisite for update-changelog
  CHANGELOG.md: Add entries for recently merged PRs
  ENH: require "distro" for python >= 3.8
  ENH: compat with python 3.8 which removed .dist -- try distro
  CLN: wtf: Remove unused (and duplicated) import
  DOC: wtf: Avoid double period in -S's description
  ENH: -D|--decor html_details -- to make it ready for pasting to github issue without clutter
  BF: assure bytes while giving to pyperclip upon its demand (on Py2)
  RF: move always present path + type "section" into "location" section, retain order of sections from cmdline
  RF: switch from nargs="*" to action=append for wtf -S
  ENH: wtf -S to specify which sections to query/display (by default -- all)
  MNT: Avoid invalid escape sequences in strings
  BF: export_to_figshare: Don't test identity of string literal
  BF(TST): do not assume user naiveness - treat any url-like looking path as a path
  BF: Check for /, \ or # in the username@hostname part while detecting SSHRI
  CHANGELOG.md: Add entry for gh-3374
  BF: revert back (remove) check for path being PathRI
  BF: list annex-transfer also in cmdline opt choice for "what"
  ...

yarikoptic added a commit that referenced this pull request May 28, 2019

Merge tag '0.11.5' into debian -- got two more minor changes
0.11.5 (May 23, 2019) -- stability is not overrated

Should be faster and less buggy, with a few enhancements.

 Fixes

- [create-sibling][]  ([#3318][])
  - Siblings are no longer configured with a post-update hook unless a
    web interface is requested with `--ui`.
  - `git submodule update --init` is no longer called from the
    post-update hook.
  - If `--inherit` is given for a dataset without a superdataset, a
    warning is now given instead of raising an error.
- The internal command runner failed on Python 2 when its `env`
  argument had unicode values.  ([#3332][])
- The safeguard that prevents creating a dataset in a subdirectory
  that already contains tracked files for another repository failed on
  Git versions before 2.14.  For older Git versions, we now warn the
  caller that the safeguard is not active.  ([#3347][])
- A regression introduced in v0.11.1 prevented [save][] from committing
  changes under a subdirectory when the subdirectory was specified as
  a path argument.  ([#3106][])
- A workaround introduced in v0.11.1 made it possible for [save][] to
  do a partial commit with an annex file that has gone below the
  `annex.largefiles` threshold.  The logic of this workaround was
  faulty, leading to files being displayed as typechanged in the index
  following the commit.  ([#3365][])
- The resolve_path() helper confused paths that had a semicolon for
  SSH RIs.  ([#3425][])
- The detection of SSH RIs has been improved.  ([#3425][])

 Enhancements and new features

- The internal command runner was too aggressive in its decision to
  sleep.  ([#3322][])
- The "INFO" label in log messages now retains the default text color
  for the terminal rather than using white, which only worked well for
  terminals with dark backgrounds.  ([#3334][])
- A short flag `-R` is now available for the `--recursion-limit` flag,
  a flag shared by several subcommands.  ([#3340][])
- The authentication logic for [create-sibling-github][] has been
  revamped and now supports 2FA.  ([#3180][])
- New configuration option `datalad.ui.progressbar` can be used to
  configure the default backend for progress reporting ("none", for
  example, results in no progress bars being shown).  ([#3396][])
- A new progress backend, available by setting datalad.ui.progressbar
  to "log", replaces progress bars with a log message upon completion
  of an action.  ([#3396][])
- DataLad learned to consult the [NO_COLOR][] environment variable and
  the new `datalad.ui.color` configuration option when deciding to
  color output.  The default value, "auto", retains the current
  behavior of coloring output if attached to a TTY ([#3407][]).
- [clean][] now removes annex transfer directories, which is useful
  for cleaning up failed downloads. ([#3374][])
- [clone][] no longer refuses to clone into a local path that looks
  like a URL, making its behavior consistent with `git clone`.
  ([#3425][])
- [wtf][]
  - Learned to fall back to the `dist` package if `platform.dist`,
    which has been removed in the yet-to-be-release Python 3.8, does
    not exist.  ([#3439][])
  - Gained a `--section` option for limiting the output to specific
    sections and a `--decor` option, which currently knows how to
    format the output as GitHub's `<details>` section.  ([#3440][])

* tag '0.11.5':
  BF: make test for url download more reliable in cases where connection fails
  RF: remove stale commented out duecredit in setup.py.  It has now its own section

@yarikoptic yarikoptic added this to the 0.11.x milestone Jul 26, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.