kopia lustrzana https://github.com/jupyterhub/repo2docker
Merge branch 'master' into change-julia-env-handling
commit
7105a8a3ef
|
@ -9,6 +9,8 @@ the configuration files found in the repository.
|
||||||
See the [repo2docker documentation](http://repo2docker.readthedocs.io)
|
See the [repo2docker documentation](http://repo2docker.readthedocs.io)
|
||||||
for more information on using repo2docker.
|
for more information on using repo2docker.
|
||||||
|
|
||||||
|
For support questions please search or post to https://discourse.jupyter.org/c/binder.
|
||||||
|
|
||||||
See the [contributing guide](CONTRIBUTING.md) for information on contributing to
|
See the [contributing guide](CONTRIBUTING.md) for information on contributing to
|
||||||
repo2docker.
|
repo2docker.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!--
|
||||||
|
Welcome!
|
||||||
|
|
||||||
|
Help us stay on top of things (and get faster replies) by submitting them to
|
||||||
|
the right place:
|
||||||
|
|
||||||
|
* for support questions please use https://discourse.jupyter.org/c/binder
|
||||||
|
* repo2docker development related issues https://github.com/jupyter/repo2docker/issues/new
|
||||||
|
* if the https://mybinder.org site is down or has inaccessible pages: https://github.com/jupyterhub/mybinder.org-deploy/issues/new
|
||||||
|
|
||||||
|
If you aren't sure where to go use https://discourse.jupyter.org/c/binder.
|
||||||
|
|
||||||
|
-->
|
|
@ -17,6 +17,8 @@ API changes
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
---------
|
---------
|
||||||
|
- Install IJulia kernel into ${NB_PYTHON_PREFIX}/share/jupyter in :pr:`622` by
|
||||||
|
:user:`davidanthoff`.
|
||||||
|
|
||||||
|
|
||||||
Version 0.8.0
|
Version 0.8.0
|
||||||
|
|
|
@ -32,6 +32,10 @@ Below is a list of supported configuration files (roughly in the order of build
|
||||||
``environment.yml`` is the standard configuration file used by `conda <https://conda.io>`_
|
``environment.yml`` is the standard configuration file used by `conda <https://conda.io>`_
|
||||||
that lets you install any kind of package,
|
that lets you install any kind of package,
|
||||||
including Python, R, and C/C++ packages.
|
including Python, R, and C/C++ packages.
|
||||||
|
``repo2docker`` does not use ``environment.yml`` to create and activate a new conda environment.
|
||||||
|
Rather, it updates a base conda environment with the packages listed in ``environment.yml``.
|
||||||
|
This means that the environment will always have the same default name, not the name
|
||||||
|
specified in ``environment.yml``.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@ Only R 3.4.4 is currently supported, which is installed via ``apt`` from the
|
||||||
`ubuntu bionic repository <https://packages.ubuntu.com/bionic/r-base>`_.
|
`ubuntu bionic repository <https://packages.ubuntu.com/bionic/r-base>`_.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Why is my repository is failing to build with ``ResolvePackageNotFound`` ?
|
Why is my repository is failing to build with ``ResolvePackageNotFound`` ?
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -147,3 +149,17 @@ notebooks (among others).
|
||||||
the container, use the ``--volumes`` option instead. Similarly,
|
the container, use the ``--volumes`` option instead. Similarly,
|
||||||
for a fully customized user Dockerfile, this option is not
|
for a fully customized user Dockerfile, this option is not
|
||||||
guaranteed to work.
|
guaranteed to work.
|
||||||
|
|
||||||
|
|
||||||
|
Why is my R shiny app not launching?
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
If you are trying to run an R shiny app using the ``/shiny/folder_containing_shiny``
|
||||||
|
url option, but the launch returns "The application exited during initialization.",
|
||||||
|
there might be something wrong with the specification of the app. One way of debugging
|
||||||
|
the app in the container is by running the ``rstudio`` url, open either the ui or
|
||||||
|
server file for the app, and run the app in the container rstudio. This way you can
|
||||||
|
see the rstudio logs as it tries to initialise the shiny app. If you a missing a
|
||||||
|
package or other dependency for the container, this will be obvious at this stage.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,10 @@ Julia
|
||||||
|
|
||||||
To build an environment with Julia, include a configuration file called
|
To build an environment with Julia, include a configuration file called
|
||||||
``Project.toml``. The format of this file is documented at
|
``Project.toml``. The format of this file is documented at
|
||||||
`the Julia Pkg.jl documentation <https://julialang.github.io/Pkg.jl/stable/>`_.
|
`the Julia Pkg.jl documentation <https://julialang.github.io/Pkg.jl/v1/>`_.
|
||||||
To specify a specific version of Julia to install, put a Julia version in the
|
To specify a specific version of Julia to install, put a Julia version in the
|
||||||
``Compat`` section of the ``Project.toml`` file, as described
|
``[compat]`` section of the ``Project.toml`` file, as described
|
||||||
here: https://julialang.github.io/Pkg.jl/stable/compatibility.
|
here: https://julialang.github.io/Pkg.jl/v1/compatibility/.
|
||||||
|
|
||||||
Languages not covered here
|
Languages not covered here
|
||||||
==========================
|
==========================
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import toml
|
import toml
|
||||||
from ..python import PythonBuildPack
|
from ..python import PythonBuildPack
|
||||||
from .julia_semver import find_semver_match
|
from .semver import find_semver_match
|
||||||
|
|
||||||
class JuliaProjectTomlBuildPack(PythonBuildPack):
|
class JuliaProjectTomlBuildPack(PythonBuildPack):
|
||||||
"""
|
"""
|
||||||
|
@ -61,7 +61,8 @@ class JuliaProjectTomlBuildPack(PythonBuildPack):
|
||||||
('JULIA_PATH', '${APP_BASE}/julia'),
|
('JULIA_PATH', '${APP_BASE}/julia'),
|
||||||
('JULIA_DEPOT_PATH', '${JULIA_PATH}/pkg'),
|
('JULIA_DEPOT_PATH', '${JULIA_PATH}/pkg'),
|
||||||
('JULIA_VERSION', self.julia_version),
|
('JULIA_VERSION', self.julia_version),
|
||||||
('JUPYTER', '${NB_PYTHON_PREFIX}/bin/jupyter')
|
('JUPYTER', '${NB_PYTHON_PREFIX}/bin/jupyter'),
|
||||||
|
('JUPYTER_DATA_DIR', '${NB_PYTHON_PREFIX}/share/jupyter'),
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_env(self):
|
def get_env(self):
|
||||||
|
@ -122,7 +123,7 @@ class JuliaProjectTomlBuildPack(PythonBuildPack):
|
||||||
(
|
(
|
||||||
"${NB_USER}",
|
"${NB_USER}",
|
||||||
r"""
|
r"""
|
||||||
JULIA_PROJECT="" julia -e "using Pkg; Pkg.add(\"IJulia\"); using IJulia; installkernel(\"Julia\", env=Dict(\"JUPYTER_DATA_DIR\"=>\"${NB_PYTHON_PREFIX}/share/jupyter\"));" && \
|
JULIA_PROJECT="" julia -e "using Pkg; Pkg.add(\"IJulia\"); using IJulia; installkernel(\"Julia\", \"--project=${REPO_DIR}\");" && \
|
||||||
julia --project=${REPO_DIR} -e 'using Pkg; Pkg.instantiate(); pkg"precompile"'
|
julia --project=${REPO_DIR} -e 'using Pkg; Pkg.instantiate(); pkg"precompile"'
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
# This file implements the julia-specific logic for handling SemVer (Semantic
|
|
||||||
# Versioning) strings in .toml files.
|
|
||||||
#
|
|
||||||
# It uses the python "semver" package to do most version string comparisons, but
|
|
||||||
# the places where julia's SemVer handling differs from the semver package have
|
|
||||||
# been implemented directly.
|
|
||||||
#
|
|
||||||
# Here, we use tuples to represent a Version, and functors as "matchers". The matcher
|
|
||||||
# functors take a version string and return true if it passes its constraints.
|
|
||||||
|
|
||||||
import re
|
|
||||||
import semver
|
|
||||||
|
|
||||||
# Main algorithm:
|
|
||||||
# Create an AbstractMatcher instance from the constraint string, and check it
|
|
||||||
# against each version in the versions_list, returning the first success.
|
|
||||||
def find_semver_match(constraint, versions_list):
|
|
||||||
matcher = create_semver_matcher(constraint)
|
|
||||||
for vstr in reversed(versions_list):
|
|
||||||
if matcher.match(str_to_version(vstr)):
|
|
||||||
return vstr
|
|
||||||
return None
|
|
||||||
|
|
||||||
def str_to_version(vstr):
|
|
||||||
return tuple([int(n) for n in vstr.split('.')])
|
|
||||||
|
|
||||||
# --- Matcher interface -------------------------------------------
|
|
||||||
|
|
||||||
class AbstractMatcher:
|
|
||||||
def match(self, v):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class SemverMatcher(AbstractMatcher):
|
|
||||||
""" Match a version tuple to a given constraint_str using the `semver` package. """
|
|
||||||
def __init__(self, constraint_str):
|
|
||||||
self.constraint_str = constraint_str
|
|
||||||
def match(self, v):
|
|
||||||
while len(v) < 3:
|
|
||||||
v = v+(0,)
|
|
||||||
v_str = '.'.join(map(str, v))
|
|
||||||
return semver.match(v_str, self.constraint_str)
|
|
||||||
def __repr__(self):
|
|
||||||
return self.constraint_str
|
|
||||||
|
|
||||||
# --- Custom matcher for julia-specific SemVer handling: ---------
|
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
class Exclusivity(Enum):
|
|
||||||
EXCLUSIVE = 1
|
|
||||||
INCLUSIVE = 2
|
|
||||||
|
|
||||||
class VersionRange(AbstractMatcher):
|
|
||||||
""" Match a version tuple between lower and upper bounds. """
|
|
||||||
def __init__(self, lower, upper, upper_exclusivity):
|
|
||||||
self.lower = lower
|
|
||||||
self.upper = upper
|
|
||||||
self.upper_exclusivity = upper_exclusivity
|
|
||||||
def match(self, v):
|
|
||||||
if self.upper_exclusivity == Exclusivity.EXCLUSIVE:
|
|
||||||
return self.lower <= v < self.upper
|
|
||||||
else:
|
|
||||||
return self.lower <= v <= self.upper
|
|
||||||
def __repr__(self):
|
|
||||||
return ("["+".".join(map(str, self.lower)) +"-"+ ".".join(map(str, self.upper)) +
|
|
||||||
(")" if self.upper_exclusivity == Exclusivity.EXCLUSIVE else "]"))
|
|
||||||
|
|
||||||
# Helpers
|
|
||||||
def major(v): return v[0]
|
|
||||||
def minor(v): return v[1] if len(v) >= 2 else 0
|
|
||||||
def patch(v): return v[2] if len(v) >= 3 else 0
|
|
||||||
|
|
||||||
# --- main constraint parser function ------------------------------------
|
|
||||||
|
|
||||||
def create_semver_matcher(constraint_str):
|
|
||||||
"""
|
|
||||||
Returns a derived-class instance of AbstractMatcher that matches version
|
|
||||||
tuples against the provided constraint_str.
|
|
||||||
"""
|
|
||||||
constraint_str = constraint_str.strip()
|
|
||||||
first_digit = re.search(r"\d", constraint_str)
|
|
||||||
if not first_digit:
|
|
||||||
# Invalid version string (no numbers in it)
|
|
||||||
return ""
|
|
||||||
constraint = str_to_version(constraint_str[first_digit.start():])
|
|
||||||
|
|
||||||
comparison_symbol = constraint_str[0:first_digit.start()].strip()
|
|
||||||
|
|
||||||
# Default to "^" search if no matching mode specified (up to next major version)
|
|
||||||
if (first_digit.start() == 0) or (comparison_symbol == "^"):
|
|
||||||
if major(constraint) == 0:
|
|
||||||
# Also, julia treats pre-1.0 releases specially, as if the first
|
|
||||||
# non-zero number is actually a major number:
|
|
||||||
# https://docs.julialang.org/en/latest/stdlib/Pkg/#Caret-specifiers-1
|
|
||||||
# So we need to handle it separately by bumping the first non-zero number.
|
|
||||||
for i,n in enumerate(constraint):
|
|
||||||
if n != 0 or i == len(constraint)-1: # (using the last existing number handles situations like "^0.0" or "^0")
|
|
||||||
upper = constraint[0:i] + (n+1,)
|
|
||||||
break
|
|
||||||
return VersionRange(constraint, upper, Exclusivity.EXCLUSIVE)
|
|
||||||
else:
|
|
||||||
return VersionRange(constraint, (major(constraint)+1,), Exclusivity.EXCLUSIVE)
|
|
||||||
|
|
||||||
# '~' matching (only allowed to bump the last present number by one)
|
|
||||||
if (comparison_symbol == "~"):
|
|
||||||
return VersionRange(constraint, constraint[:-1] +(constraint[-1]+1,), Exclusivity.INCLUSIVE)
|
|
||||||
|
|
||||||
# Use semver package's comparisons for everything else:
|
|
||||||
|
|
||||||
# semver requires three version numbers
|
|
||||||
if len(constraint) < 3:
|
|
||||||
while len(constraint) < 3:
|
|
||||||
constraint = constraint+(0,)
|
|
||||||
constraint_str = constraint_str[0:first_digit.start()] + ".".join(map(str, constraint))
|
|
||||||
|
|
||||||
# Convert special comparison strings to format accepted by `semver` library.
|
|
||||||
constraint_str = constraint_str.replace("≥", ">=").replace("≤", "<=")
|
|
||||||
constraint_str = re.sub(r"(^|\b)=\b", "==", constraint_str)
|
|
||||||
|
|
||||||
return SemverMatcher(constraint_str)
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
"""
|
||||||
|
Julia specific handling of SemVer strings
|
||||||
|
|
||||||
|
It uses the python "semver" package to do most version string comparisons, but
|
||||||
|
the places where julia's SemVer handling differs from the semver package have
|
||||||
|
been implemented directly.
|
||||||
|
|
||||||
|
We use tuples to represent a Version, and functors as "matchers". The
|
||||||
|
matcher functors take a version string and return True if it passes its
|
||||||
|
constraints.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
import semver
|
||||||
|
|
||||||
|
|
||||||
|
def find_semver_match(constraint, versions_list):
|
||||||
|
matcher = create_semver_matcher(constraint)
|
||||||
|
for vstr in reversed(versions_list):
|
||||||
|
if matcher.match(str_to_version(vstr)):
|
||||||
|
return vstr
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def str_to_version(vstr):
|
||||||
|
return tuple([int(n) for n in vstr.split(".")])
|
||||||
|
|
||||||
|
|
||||||
|
# Helpers
|
||||||
|
def major(v):
|
||||||
|
return v[0]
|
||||||
|
|
||||||
|
|
||||||
|
def minor(v):
|
||||||
|
return v[1] if len(v) >= 2 else 0
|
||||||
|
|
||||||
|
|
||||||
|
def patch(v):
|
||||||
|
return v[2] if len(v) >= 3 else 0
|
||||||
|
|
||||||
|
|
||||||
|
def create_semver_matcher(constraint_str):
|
||||||
|
"""Create a matcher that can be used to match version tuples
|
||||||
|
|
||||||
|
Version tuples are matched against the provided regex `constraint_str`.
|
||||||
|
"""
|
||||||
|
constraint_str = constraint_str.strip()
|
||||||
|
first_digit = re.search(r"\d", constraint_str)
|
||||||
|
if not first_digit:
|
||||||
|
# Invalid version string (no numbers in it)
|
||||||
|
return ""
|
||||||
|
constraint = str_to_version(constraint_str[first_digit.start() :])
|
||||||
|
|
||||||
|
comparison_symbol = constraint_str[0 : first_digit.start()].strip()
|
||||||
|
|
||||||
|
# Default to "^" search if no matching mode specified (up to next major version)
|
||||||
|
if (first_digit.start() == 0) or (comparison_symbol == "^"):
|
||||||
|
if major(constraint) == 0:
|
||||||
|
# Also, julia treats pre-1.0 releases specially, as if the first
|
||||||
|
# non-zero number is actually a major number:
|
||||||
|
# https://docs.julialang.org/en/latest/stdlib/Pkg/#Caret-specifiers-1
|
||||||
|
# So we need to handle it separately by bumping the first non-zero
|
||||||
|
# enumber.
|
||||||
|
for i, n in enumerate(constraint):
|
||||||
|
if (
|
||||||
|
n != 0 or i == len(constraint) - 1
|
||||||
|
): # (using the last existing number handles situations like "^0.0" or "^0")
|
||||||
|
upper = constraint[0:i] + (n + 1,)
|
||||||
|
break
|
||||||
|
return VersionRange(constraint, upper, True)
|
||||||
|
else:
|
||||||
|
return VersionRange(
|
||||||
|
constraint, (major(constraint) + 1,), True
|
||||||
|
)
|
||||||
|
|
||||||
|
# '~' matching (only allowed to bump the last present number by one)
|
||||||
|
if comparison_symbol == "~":
|
||||||
|
return VersionRange(
|
||||||
|
constraint,
|
||||||
|
constraint[:-1] + (constraint[-1] + 1,),
|
||||||
|
exclusive=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use semver package's comparisons for everything else:
|
||||||
|
|
||||||
|
# semver requires three version numbers
|
||||||
|
if len(constraint) < 3:
|
||||||
|
while len(constraint) < 3:
|
||||||
|
constraint = constraint + (0,)
|
||||||
|
constraint_str = constraint_str[0 : first_digit.start()] + ".".join(
|
||||||
|
map(str, constraint)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert special comparison strings to format accepted by `semver` library.
|
||||||
|
constraint_str = constraint_str.replace("≥", ">=").replace("≤", "<=")
|
||||||
|
constraint_str = re.sub(r"(^|\b)=\b", "==", constraint_str)
|
||||||
|
|
||||||
|
return SemverMatcher(constraint_str)
|
||||||
|
|
||||||
|
|
||||||
|
class SemverMatcher:
|
||||||
|
"""Provides a utility for using `semver` package to do version matching.
|
||||||
|
|
||||||
|
The `SemverMatcher` takes a `constraint_str` to represent a regex to
|
||||||
|
determine if a version tuple matches the constraint.
|
||||||
|
|
||||||
|
The matching is handled via the `semver` package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, constraint_str):
|
||||||
|
self.constraint_str = constraint_str
|
||||||
|
|
||||||
|
def match(self, v):
|
||||||
|
"""Check if `v` matches the constraint"""
|
||||||
|
while len(v) < 3:
|
||||||
|
v = v + (0,)
|
||||||
|
v_str = ".".join(map(str, v))
|
||||||
|
return semver.match(v_str, self.constraint_str)
|
||||||
|
|
||||||
|
def __eq__(self, rhs):
|
||||||
|
return self.constraint_str == rhs.constraint_str
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.constraint_str
|
||||||
|
|
||||||
|
|
||||||
|
class VersionRange:
|
||||||
|
"""Represents a range of release versions.
|
||||||
|
|
||||||
|
A `VersionRange` contains versions from a `lower` to `upper` bound
|
||||||
|
which may be inclusive (default: `exclusive=False`) or exclusive (`exclusive=True`).
|
||||||
|
|
||||||
|
A release version (represented by a tuple) can be checked to see if it
|
||||||
|
falls within a `VersionRange`
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, lower, upper, exclusive=False):
|
||||||
|
self.lower = lower
|
||||||
|
self.upper = upper
|
||||||
|
self.exclusive = exclusive
|
||||||
|
|
||||||
|
def match(self, v):
|
||||||
|
"""Check if `v` falls into the version range"""
|
||||||
|
if self.exclusive:
|
||||||
|
return self.lower <= v < self.upper
|
||||||
|
else:
|
||||||
|
return self.lower <= v <= self.upper
|
||||||
|
|
||||||
|
def __eq__(self, rhs):
|
||||||
|
return (
|
||||||
|
self.lower == rhs.lower
|
||||||
|
and self.upper == rhs.upper
|
||||||
|
and self.exclusive == rhs.exclusive
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
"["
|
||||||
|
+ ".".join(map(str, self.lower))
|
||||||
|
+ "-"
|
||||||
|
+ ".".join(map(str, self.upper))
|
||||||
|
+ (")" if self.exclusive else "]")
|
||||||
|
)
|
|
@ -14,4 +14,12 @@ catch
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Verify that kernels are not installed in home directory (issue #620)
|
||||||
|
try
|
||||||
|
using IJulia
|
||||||
|
@assert IJulia.kerneldir() == "/srv/conda/share/jupyter/kernels"
|
||||||
|
catch
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
|
@ -14,4 +14,12 @@ catch
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Verify that kernels are not installed in home directory (issue #620)
|
||||||
|
try
|
||||||
|
using IJulia
|
||||||
|
@assert IJulia.kerneldir() == "/srv/conda/share/jupyter/kernels"
|
||||||
|
catch
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
from repo2docker.buildpacks.julia import semver
|
||||||
|
|
||||||
|
|
||||||
|
def test_str_to_version():
|
||||||
|
assert semver.str_to_version("1.5.2") == (1, 5, 2)
|
||||||
|
assert semver.str_to_version("1") == (1,)
|
||||||
|
|
||||||
|
|
||||||
|
def test_major_minor_patch():
|
||||||
|
V = (1, 2, 3)
|
||||||
|
assert (semver.major(V), semver.minor(V), semver.patch(V)) == (1, 2, 3)
|
||||||
|
assert semver.major((1,)) == 1
|
||||||
|
assert semver.minor((1,)) == 0
|
||||||
|
assert semver.patch((1,)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_simple_matches():
|
||||||
|
assert repr(semver.create_semver_matcher("1.2.5")) == "[1.2.5-2)"
|
||||||
|
assert repr(semver.create_semver_matcher("1.2.5")) == "[1.2.5-2)"
|
||||||
|
assert repr(semver.create_semver_matcher("^1.2.3")) == "[1.2.3-2)"
|
||||||
|
assert repr(semver.create_semver_matcher("^1.2")) == "[1.2-2)"
|
||||||
|
assert repr(semver.create_semver_matcher("^1")) == "[1-2)"
|
||||||
|
assert repr(semver.create_semver_matcher("^0.2.3")) == "[0.2.3-0.3)"
|
||||||
|
assert repr(semver.create_semver_matcher("^0.0.3")) == "[0.0.3-0.0.4)"
|
||||||
|
assert repr(semver.create_semver_matcher("^0.0")) == "[0.0-0.1)"
|
||||||
|
assert repr(semver.create_semver_matcher("^0")) == "[0-1)"
|
||||||
|
# This one seems wrong: `~1.2.3 = [1.2.3, 1.2.4)` but ~ is special in Julia
|
||||||
|
# from https://docs.julialang.org/en/latest/stdlib/Pkg/#Tilde-specifiers-1
|
||||||
|
assert repr(semver.create_semver_matcher("~1.2.3")) == "[1.2.3-1.2.4]"
|
||||||
|
assert repr(semver.create_semver_matcher("~1.3.5")) == "[1.3.5-1.3.6]"
|
||||||
|
assert repr(semver.create_semver_matcher("~1.2")) == "[1.2-1.3]"
|
||||||
|
assert repr(semver.create_semver_matcher("~1")) == "[1-2]"
|
||||||
|
|
||||||
|
|
||||||
|
def test_range_matches():
|
||||||
|
assert semver.create_semver_matcher(
|
||||||
|
"1.2.3"
|
||||||
|
) == semver.create_semver_matcher("^1.2.3")
|
||||||
|
assert semver.create_semver_matcher(
|
||||||
|
"1.2.3"
|
||||||
|
) == semver.create_semver_matcher("^1.2.3")
|
||||||
|
assert semver.create_semver_matcher("1.2") == semver.create_semver_matcher(
|
||||||
|
"^1.2"
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("1") == semver.create_semver_matcher(
|
||||||
|
"^1"
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher(
|
||||||
|
"0.0.3"
|
||||||
|
) == semver.create_semver_matcher("^0.0.3")
|
||||||
|
assert semver.create_semver_matcher("0") == semver.create_semver_matcher(
|
||||||
|
"^0"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_match_particular_version():
|
||||||
|
assert semver.create_semver_matcher("1.2.3").match(
|
||||||
|
semver.str_to_version("1.5.2")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.3")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("1.2.3").match(
|
||||||
|
semver.str_to_version("2.0.0")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.2")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("~1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.4")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("~1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.3")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("~1.2.3").match(
|
||||||
|
semver.str_to_version("1.3")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("1.2").match(
|
||||||
|
semver.str_to_version("1.2.0")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("1.2").match(
|
||||||
|
semver.str_to_version("1.9.9")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("1.2").match(
|
||||||
|
semver.str_to_version("2.0.0")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("1.2").match(
|
||||||
|
semver.str_to_version("1.1.9")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("0.2.3").match(
|
||||||
|
semver.str_to_version("0.2.3")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("0.2.3").match(
|
||||||
|
semver.str_to_version("0.3.0")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("0.2.3").match(
|
||||||
|
semver.str_to_version("0.2.2")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("0").match(
|
||||||
|
semver.str_to_version("0.0.0")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("0").match(
|
||||||
|
semver.str_to_version("0.99.0")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("0").match(semver.str_to_version("1.0.0"))
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("0.0").match(
|
||||||
|
semver.str_to_version("0.0.0")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("0.0").match(
|
||||||
|
semver.str_to_version("0.0.99")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("0.0").match(
|
||||||
|
semver.str_to_version("0.1.0")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_less_than_prefix():
|
||||||
|
assert repr(semver.create_semver_matcher("<1.2.3")) == "<1.2.3"
|
||||||
|
assert repr(semver.create_semver_matcher("<1")) == "<1.0.0"
|
||||||
|
assert repr(semver.create_semver_matcher("<0.2.3")) == "<0.2.3"
|
||||||
|
|
||||||
|
assert semver.create_semver_matcher("<2.0.3").match(
|
||||||
|
semver.str_to_version("2.0.2")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("<2").match(
|
||||||
|
semver.str_to_version("0.0.1")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher("<2.0.3").match(
|
||||||
|
semver.str_to_version("0.2.3")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("<0.2.4").match(
|
||||||
|
semver.str_to_version("0.2.4")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_equal_prefix():
|
||||||
|
assert repr(semver.create_semver_matcher("=1.2.3")) == "==1.2.3"
|
||||||
|
assert repr(semver.create_semver_matcher("=1.2")) == "==1.2.0"
|
||||||
|
assert repr(semver.create_semver_matcher(" =1")) == "==1.0.0"
|
||||||
|
assert semver.create_semver_matcher("=1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.3")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("=1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.4")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher("=1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.2")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_fancy_unicode():
|
||||||
|
assert semver.create_semver_matcher(
|
||||||
|
"≥1.3.0"
|
||||||
|
) == semver.create_semver_matcher(">=1.3.0")
|
||||||
|
|
||||||
|
|
||||||
|
def test_largerthan_equal():
|
||||||
|
assert repr(semver.create_semver_matcher(">= 1.2.3")) == ">= 1.2.3"
|
||||||
|
assert repr(semver.create_semver_matcher(" >= 1")) == ">= 1.0.0"
|
||||||
|
assert semver.create_semver_matcher(">=1").match(
|
||||||
|
semver.str_to_version("1.0.0")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher(">=0").match(
|
||||||
|
semver.str_to_version("0.0.1")
|
||||||
|
)
|
||||||
|
assert semver.create_semver_matcher(">=1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.3")
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
semver.create_semver_matcher(">=1.2.3").match(
|
||||||
|
semver.str_to_version("1.2.2")
|
||||||
|
)
|
||||||
|
== False
|
||||||
|
)
|
Ładowanie…
Reference in New Issue