fades-master/ 0000775 0001750 0001750 00000000000 15215454131 012434 5 ustar tchet tchet fades-master/.snapcraft.yaml 0000777 0001750 0001750 00000000000 15250736520 021745 2pkg/snap/snapcraft.yaml ustar tchet tchet fades-master/tests/ 0000775 0001750 0001750 00000000000 15215454131 013576 5 ustar tchet tchet fades-master/tests/test_pkgnamesdb.py 0000664 0001750 0001750 00000001677 15215454131 017335 0 ustar tchet tchet # Copyright 2020 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for the package names DB."""
from fades import pkgnamesdb
def test_db_consistency():
"""Ensure multiple DB entrypoints are consistent between them."""
assert len(pkgnamesdb.MODULE_TO_PACKAGE) == len(pkgnamesdb.PACKAGE_TO_MODULE)
fades-master/tests/test_pipmanager.py 0000664 0001750 0001750 00000015461 15215454131 017341 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for pip related code."""
import os
import io
import pytest
from pathlib import Path
from unittest.mock import patch, call
from fades.pipmanager import PipManager
from fades import pipmanager
from fades import helpers
BIN_PATH = Path("somepath")
def test_get_parsing_ok_pytest():
mocked_stdout = [
"Name: foo",
"Version: 2.0.0",
"Location: ~/.local/share/fades/86cc492/lib/python3.4/site-packages",
"Requires: ",
]
mgr = PipManager(BIN_PATH, pip_installed=True)
with patch.object(helpers, "logged_exec", return_value=mocked_stdout):
version = mgr.get_version("foo")
assert version, "2.0.0"
def test_get_parsing_error(logs):
mocked_stdout = [
"Name: foo",
"Release: 2.0.0",
"Location: ~/.local/share/fades/86cc492/lib/python3.4/site-packages",
"Requires: ",
]
mgr = PipManager(BIN_PATH, pip_installed=True)
with patch.object(helpers, "logged_exec", return_value=mocked_stdout):
version = mgr.get_version("foo")
assert version == ""
assert (
'Fades is having problems getting the installed version. '
'Run with -v or check the logs for details'
) in logs.error
def test_real_case_levenshtein():
mocked_stdout = [
"Metadata-Version: 1.1",
"Name: python-Levenshtein",
"Version: 0.12.0",
"License: GPL",
]
mgr = PipManager(BIN_PATH, pip_installed=True)
with patch.object(helpers, "logged_exec", return_value=mocked_stdout):
version = mgr.get_version("foo")
assert version == "0.12.0"
def test_install():
mgr = PipManager(BIN_PATH, pip_installed=True)
pip_path = Path(BIN_PATH) / "pip"
with patch.object(helpers, "logged_exec") as mock:
mgr.install("foo")
# check it always upgrades pip, and then the proper install
python_path = Path(BIN_PATH) / "python"
c1 = call([python_path, "-m", "pip", "install", "pip", "--upgrade"])
c2 = call([pip_path, "install", "foo"])
assert mock.call_args_list == [c1, c2]
def test_install_without_pip_upgrade():
mgr = PipManager(BIN_PATH, pip_installed=True, avoid_pip_upgrade=True)
pip_path = Path(BIN_PATH) / "pip"
with patch.object(helpers, "logged_exec") as mock:
mgr.install("foo")
mock.assert_called_with([pip_path, "install", "foo"])
def test_install_multiword_dependency():
mgr = PipManager(BIN_PATH, pip_installed=True)
pip_path = Path(BIN_PATH) / "pip"
with patch.object(helpers, "logged_exec") as mock:
mgr.install("foo bar")
mock.assert_called_with([pip_path, "install", "foo", "bar"])
def test_install_with_options():
mgr = PipManager(BIN_PATH, pip_installed=True, options=["--bar baz"])
pip_path = Path(BIN_PATH) / "pip"
with patch.object(helpers, "logged_exec") as mock:
mgr.install("foo")
mock.assert_called_with([pip_path, "install", "foo", "--bar", "baz"])
def test_install_with_options_using_equal():
mgr = PipManager(BIN_PATH, pip_installed=True, options=["--bar=baz"])
pip_path = Path(BIN_PATH) / "pip"
with patch.object(helpers, "logged_exec") as mock:
mgr.install("foo")
mock.assert_called_with([pip_path, "install", "foo", "--bar=baz"])
def test_install_raise_error(logs):
mgr = PipManager(BIN_PATH, pip_installed=True)
with patch.object(helpers, "logged_exec", side_effect=['ok', Exception("Kapow!")]):
with pytest.raises(Exception):
mgr.install("foo")
assert "Error installing foo: Kapow!" in logs.error
def test_install_without_pip():
mgr = PipManager(BIN_PATH, pip_installed=False)
pip_path = Path(BIN_PATH) / "pip"
with patch.object(helpers, "logged_exec") as mocked_exec:
with patch.object(mgr, "_brute_force_install_pip") as mocked_install_pip:
mgr.install("foo")
assert mocked_install_pip.call_count == 1
mocked_exec.assert_called_with([pip_path, "install", "foo"])
def test_brute_force_install_pip_installer_exists(tmp_path):
tmp_file = tmp_path / "hello.txt"
mgr = PipManager(BIN_PATH, pip_installed=False)
python_path = Path(BIN_PATH) / "python"
# get the tempfile but leave it there to be found
open(tmp_file, 'wt', encoding='utf8').close()
mgr.pip_installer_fname = Path(tmp_file)
with patch.object(helpers, "logged_exec") as mocked_exec:
with patch.object(mgr, "_download_pip_installer") as download_installer:
mgr._brute_force_install_pip()
assert not download_installer.called
mocked_exec.assert_called_with([python_path, mgr.pip_installer_fname, "-I"])
assert mgr.pip_installed
def test_brute_force_install_pip_no_installer(tmp_path):
tmp_file = tmp_path / "hello.txt"
mgr = PipManager(BIN_PATH, pip_installed=False)
python_path = Path(BIN_PATH) / "python"
mgr.pip_installer_fname = Path(tmp_file)
with patch.object(helpers, "logged_exec") as mocked_exec:
with patch.object(mgr, "_download_pip_installer") as download_installer:
mgr._brute_force_install_pip()
download_installer.assert_called_once_with()
mocked_exec.assert_called_with([python_path, mgr.pip_installer_fname, "-I"])
assert mgr.pip_installed
def test_download_pip_installer(tmp_path):
tmp_file = tmp_path / "hello.txt"
mgr = PipManager(BIN_PATH, pip_installed=False)
mgr.pip_installer_fname = tmp_file
with patch("fades.pipmanager.request.urlopen", return_value=io.BytesIO(b"hola")) as urlopen:
mgr._download_pip_installer()
assert os.path.exists(mgr.pip_installer_fname)
urlopen.assert_called_once_with(pipmanager.PIP_INSTALLER)
def test_freeze(tmp_path):
tmp_file = tmp_path / "reqtest.txt"
# call and check pip was executed ok
mgr = PipManager(BIN_PATH)
with patch.object(helpers, "logged_exec") as mock:
mock.return_value = ['moño>11', 'foo==1.2'] # "bad" order, on purpose
mgr.freeze(tmp_file)
pip_path = Path(BIN_PATH) / "pip"
mock.assert_called_with([pip_path, "freeze", "--all", "--local"])
# check results were stored properly
with open(tmp_file, 'rt', encoding='utf8') as fh:
stored = fh.read()
assert stored == 'foo==1.2\nmoño>11\n'
fades-master/tests/test_parsing/ 0000775 0001750 0001750 00000000000 15215454131 016300 5 ustar tchet tchet fades-master/tests/test_parsing/test_vcs_dependency.py 0000664 0001750 0001750 00000001450 15215454131 022702 0 ustar tchet tchet """Check the VCSDependency."""
from fades import parsing
def test_string_representation():
"""This is particularly tested because it's the interface to be installed."""
dep = parsing.VCSDependency("testurl")
assert str(dep), "testurl"
def test_contains():
"""This is particularly tested because it's how fulfilling is tested."""
dep1 = parsing.VCSDependency("testurl")
assert dep1.specifier.contains(None)
assert not dep1.specifier.contains("123")
def test_equality():
dep1 = parsing.VCSDependency("testurl")
dep2 = parsing.VCSDependency("testurl")
dep3 = parsing.VCSDependency("otherurl")
assert dep1 == dep2
assert not (dep1 == dep3)
assert not (dep1 != dep2)
assert dep1 != dep3
assert not (dep1 == 123)
assert not (dep1 == "testurl")
fades-master/tests/test_parsing/test_reqs.py 0000664 0001750 0001750 00000005726 15215454131 020675 0 ustar tchet tchet """Check the requirements parsing."""
import io
from logassert import Multiple
from fades import parsing, REPO_PYPI, REPO_VCS
from tests import get_reqs
def test_empty():
parsed = parsing._parse_requirement(io.StringIO("""
"""))
assert parsed == {}
def test_simple():
parsed = parsing._parse_requirement(io.StringIO("""
pypi::foo
"""))
assert parsed == {REPO_PYPI: get_reqs('foo')}
def test_simple_default():
parsed = parsing._parse_requirement(io.StringIO("""
foo
"""))
assert parsed == {REPO_PYPI: get_reqs('foo')}
def test_double():
parsed = parsing._parse_requirement(io.StringIO("""
pypi::time
foo
"""))
assert parsed == {
REPO_PYPI: get_reqs('time') + get_reqs('foo')
}
def test_version_same():
parsed = parsing._parse_requirement(io.StringIO("""
pypi::foo == 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_same_default():
parsed = parsing._parse_requirement(io.StringIO("""
foo == 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_different():
parsed = parsing._parse_requirement(io.StringIO("""
foo !=3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo !=3.5')
}
def test_version_same_no_spaces():
parsed = parsing._parse_requirement(io.StringIO("""
foo==3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo ==3.5')
}
def test_version_greater_two_spaces():
parsed = parsing._parse_requirement(io.StringIO("""
foo > 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_version_same_or_greater():
parsed = parsing._parse_requirement(io.StringIO("""
foo >=2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >= 2')
}
def test_comments():
parsed = parsing._parse_requirement(io.StringIO("""
pypi::foo # some text
# other text
bar
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo') + get_reqs('bar')
}
def test_strange_repo(logs):
parsed = parsing._parse_requirement(io.StringIO("""
unknown::foo
"""))
assert Multiple("Not understood fades repository", "unknown") in logs.warning
assert parsed == {}
def test_vcs_simple():
parsed = parsing._parse_requirement(io.StringIO("""
vcs::strangeurl
"""))
assert parsed == {REPO_VCS: [parsing.VCSDependency("strangeurl")]}
def test_vcs_simple_default():
parsed = parsing._parse_requirement(io.StringIO("""
bzrhttp://server/bleh
"""))
assert parsed == {REPO_VCS: [parsing.VCSDependency("bzrhttp://server/bleh")]}
def test_mixed():
parsed = parsing._parse_requirement(io.StringIO("""
vcs::strangeurl
pypi::foo
"""))
assert parsed == {
REPO_VCS: [parsing.VCSDependency("strangeurl")],
REPO_PYPI: get_reqs('foo'),
}
fades-master/tests/test_parsing/test_manual.py 0000664 0001750 0001750 00000002777 15215454131 021203 0 ustar tchet tchet """Tests for the check of the manual parsing."""
from fades import parsing, REPO_PYPI, REPO_VCS
from tests import get_reqs
def test_none():
parsed = parsing.parse_manual(None)
assert parsed == {}
def test_nothing():
parsed = parsing.parse_manual([])
assert parsed == {}
def test_simple():
parsed = parsing.parse_manual(["pypi::foo"])
assert parsed == {REPO_PYPI: get_reqs("foo")}
def test_simple_default_pypi():
parsed = parsing.parse_manual(["foo"])
assert parsed == {REPO_PYPI: get_reqs("foo")}
def test_double():
parsed = parsing.parse_manual(["pypi::foo", "pypi::bar"])
assert parsed == {REPO_PYPI: get_reqs("foo", "bar")}
def test_version():
parsed = parsing.parse_manual(["pypi::foo == 3.5"])
assert parsed == {REPO_PYPI: get_reqs("foo == 3.5")}
def test_version_default():
parsed = parsing.parse_manual(["foo == 3.5"])
assert parsed == {REPO_PYPI: get_reqs("foo == 3.5")}
def test_vcs_simple():
url = "git+git://server.com/etc"
parsed = parsing.parse_manual(["vcs::" + url])
assert parsed == {REPO_VCS: [parsing.VCSDependency(url)]}
def test_vcs_simple_default():
url = "git+git://server.com/etc"
parsed = parsing.parse_manual([url])
assert parsed == {REPO_VCS: [parsing.VCSDependency(url)]}
def test_mixed():
parsed = parsing.parse_manual(["pypi::foo", "vcs::git+git://server.com/etc"])
assert parsed == {
REPO_PYPI: get_reqs("foo"),
REPO_VCS: [parsing.VCSDependency("git+git://server.com/etc")],
}
fades-master/tests/test_parsing/test_file_reqs.py 0000664 0001750 0001750 00000005313 15215454131 021664 0 ustar tchet tchet # Copyright 2019-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Check the requirements parsing from a reqs.txt file."""
import os
from fades import parsing, REPO_PYPI
from tests import get_reqs
def test_requirement_files(create_tmpfile):
parsed = parsing.parse_reqfile(create_tmpfile(['foo']))
assert parsed == {REPO_PYPI: get_reqs('foo')}
def test_nested_requirement_files(create_tmpfile):
requirement_file = create_tmpfile(['foo'])
requirement_file_nested = create_tmpfile(
['bar\n-r {}'.format(requirement_file)]
)
parsed = parsing.parse_reqfile(requirement_file_nested)
assert parsed == {REPO_PYPI: get_reqs('bar', 'foo')}
def test_nested_requirement_files_invalid_format(logs, create_tmpfile):
requirement_file_nested = create_tmpfile(['foo\n-r'])
parsed = parsing.parse_reqfile(requirement_file_nested)
assert parsed == {REPO_PYPI: get_reqs('foo')}
assert "Invalid format to indicate a nested requirements file:" in logs.warning
def test_nested_requirement_files_not_pwd(create_tmpfile):
requirement_file = create_tmpfile(['foo'])
fname = os.path.basename(requirement_file)
requirement_file_nested = create_tmpfile(
['bar\n-r {}'.format(fname)])
parsed = parsing.parse_reqfile(requirement_file_nested)
assert parsed, {REPO_PYPI: get_reqs('bar', 'foo')}
def test_nested_requirement_files_first_line(create_tmpfile):
requirement_file = create_tmpfile(['foo'])
requirement_file_nested = create_tmpfile(
['\n-r {}\nbar'.format(requirement_file)])
parsed = parsing.parse_reqfile(requirement_file_nested)
assert parsed == {REPO_PYPI: get_reqs('foo', 'bar')}
def test_two_nested_requirement_files(create_tmpfile):
requirement_file = create_tmpfile(['foo'])
requirement_file_nested1 = create_tmpfile(
['bar\n-r {}'.format(requirement_file)])
requirement_file_nested2 = create_tmpfile(
['baz\n-r {}'.format(requirement_file_nested1)])
parsed = parsing.parse_reqfile(requirement_file_nested2)
assert parsed == {REPO_PYPI: get_reqs('baz', 'bar', 'foo')}
fades-master/tests/test_parsing/test_file.py 0000664 0001750 0001750 00000027620 15215454131 020637 0 ustar tchet tchet """Check the imports parsing."""
import io
from logassert import Exact
from fades import parsing, REPO_PYPI, REPO_VCS
from tests import get_reqs
def test_nocomment():
# note that we're testing the import at the beginning of the line, and
# in also indented
parsed = parsing._parse_content(io.StringIO("""import time
import time
from time import foo
"""))
assert parsed == {}
def test_simple_default():
parsed = parsing._parse_content(io.StringIO("""
import time
import foo # fades
"""))
assert parsed == {REPO_PYPI: get_reqs('foo')}
def test_double():
parsed = parsing._parse_content(io.StringIO("""
import time # fades
import foo # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('time') + get_reqs('foo')
}
def test_version_same_default():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades == 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_different():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades !=3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo !=3.5')
}
def test_version_same_no_spaces():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades==3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo ==3.5')
}
def test_version_same_two_spaces():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades == 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_same_one_space_before():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades == 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_same_two_space_before():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades == 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_same_one_space_after():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades== 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_same_two_space_after():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades== 3.5
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo == 3.5')
}
def test_version_greater():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades > 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_version_greater_no_space():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades>2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >2')
}
def test_version_greater_no_space_default():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades>2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >2')
}
def test_version_greater_two_spaces():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades > 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_version_greater_one_space_after():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades> 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_version_greater_two_space_after():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades> 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_version_greater_one_space_before():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades> 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_version_greater_two_space_before():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades> 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_version_same_or_greater():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades >= 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >= 2')
}
def test_version_same_or_greater_no_spaces():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades>=2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >= 2')
}
def test_version_same_or_greater_one_space_before():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades >=2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >=2')
}
def test_version_same_or_greater_two_space_before():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades >=2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >=2')
}
def test_version_same_or_greater_one_space_after():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades>= 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >= 2')
}
def test_version_same_or_greater_two_space_after():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades>= 2
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo >= 2')
}
def test_continuation_line():
parsed = parsing._parse_content(io.StringIO("""
import bar
# fades > 2
import foo
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo > 2')
}
def test_from_import_simple():
parsed = parsing._parse_content(io.StringIO("""
from foo import bar # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo')
}
def test_import():
parsed = parsing._parse_content(io.StringIO("""
import foo.bar # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo')
}
def test_from_import_complex():
parsed = parsing._parse_content(io.StringIO("""
from baz.foo import bar # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('baz')
}
def test_allow_other_comments():
parsed = parsing._parse_content(io.StringIO("""
from foo import * # NOQA # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo')
}
def test_allow_other_comments_reverse_default():
parsed = parsing._parse_content(io.StringIO("""
from foo import * # fades # NOQA
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo')
}
def test_strange_import(logs):
parsed = parsing._parse_content(io.StringIO("""
from foo bar import :( # fades
"""))
assert Exact(
"Not understood import info: ['from', 'foo', 'bar', 'import', ':(']") in logs.debug
assert parsed == {}
def test_strange_fadesinfo(logs):
parsed = parsing._parse_content(io.StringIO("""
import foo # fades broken::whatever
"""))
assert "Not understood fades repository: 'broken'" in logs.warning
assert parsed == {}
def test_strange_fadesinfo2(logs):
parsed = parsing._parse_content(io.StringIO("""
import foo # fadesbroken
"""))
assert "Not understood fades info: 'fadesbroken'" in logs.warning
assert parsed == {}
def test_projectname_noversion_implicit():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades othername
"""))
assert parsed == {
REPO_PYPI: get_reqs('othername')
}
def test_projectname_noversion_explicit():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades pypi::othername
"""))
assert parsed == {
REPO_PYPI: get_reqs('othername')
}
def test_projectname_version_explicit():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades pypi::othername >= 3
"""))
assert parsed == {
REPO_PYPI: get_reqs('othername >= 3')
}
def test_projectname_version_nospace():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades othername==5
"""))
assert parsed == {
REPO_PYPI: get_reqs('othername==5')
}
def test_projectname_version_space():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades othername <5
"""))
assert parsed == {
REPO_PYPI: get_reqs('othername <5')
}
def test_projectname_pkgnamedb():
parsed = parsing._parse_content(io.StringIO("""
import bs4 # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('beautifulsoup4')
}
def test_projectname_pkgnamedb_version():
parsed = parsing._parse_content(io.StringIO("""
import bs4 # fades >=5
"""))
assert parsed == {
REPO_PYPI: get_reqs('beautifulsoup4 >=5')
}
def test_projectname_pkgnamedb_othername_default():
parsed = parsing._parse_content(io.StringIO("""
import bs4 # fades othername
"""))
assert parsed == {
REPO_PYPI: get_reqs('othername')
}
def test_projectname_pkgnamedb_version_othername():
parsed = parsing._parse_content(io.StringIO("""
import bs4 # fades othername >=5
"""))
assert parsed == {
REPO_PYPI: get_reqs('othername >=5')
}
def test_comma_separated_import():
parsed = parsing._parse_content(io.StringIO("""
from foo import bar, baz, qux # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('foo')
}
def test_other_lines_with_fades_string():
parsed = parsing._parse_content(io.StringIO("""
import bar # fades
print("screen fades to black")
"""))
assert parsed == {
REPO_PYPI: get_reqs('bar')
}
def test_commented_line(logs):
parsed = parsing._parse_content(io.StringIO("""
#import foo # fades
"""))
assert parsed == {}
assert "Not understood fades" not in logs.warning
def test_with_fades_commented_line(logs):
parsed = parsing._parse_content(io.StringIO("""
#import foo # fades
import bar # fades
"""))
assert parsed == {
REPO_PYPI: get_reqs('bar')
}
assert "Not understood fades" not in logs.warning
def test_with_commented_line(logs):
parsed = parsing._parse_content(io.StringIO("""
import bar # fades
# a commented line
"""))
assert parsed == {
REPO_PYPI: get_reqs('bar')
}
assert "Not understood fades" not in logs.warning
def test_vcs_explicit():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades vcs::superurl
"""))
assert parsed == {
REPO_VCS: [parsing.VCSDependency('superurl')]
}
def test_vcs_implicit():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades http://www.whatever/project
"""))
assert parsed == {
REPO_VCS: [parsing.VCSDependency('http://www.whatever/project')]
}
def test_mixed():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades vcs::superurl
import bar # fades
"""))
assert parsed == {
REPO_VCS: [parsing.VCSDependency('superurl')],
REPO_PYPI: get_reqs('bar'),
}
def test_fades_and_hashtag_mentioned_in_code():
"""Test the case where a string contains both: fades and hashtag (#)
but is not an import.
"""
parsed = parsing._parse_content(io.StringIO("""
'http://fades.readthedocs.io/en/release-7-0/readme.html#how-to-use-it'
"""))
assert parsed == {}
def test_fades_and_hashtag_mentioned_in_code_mixed_with_imports():
parsed = parsing._parse_content(io.StringIO("""import requests # fades
'http://fades.readthedocs.io/en/release-7-0/readme.html#how-to-use-it'
"""))
assert parsed == {
REPO_PYPI: get_reqs('requests')
}
def test_fades_user_strange_comment_with_hashtag_ignored():
parsed = parsing._parse_content(io.StringIO("""
import foo # fades==2 # Some comment with #hashtash
"""))
assert parsed == {}
fades-master/tests/test_parsing/test_docstrings.py 0000664 0001750 0001750 00000004171 15215454131 022073 0 ustar tchet tchet """Check the docstring parsing."""
import io
from fades import parsing, REPO_PYPI, REPO_VCS
from tests import get_reqs
def test_empty():
parsed = parsing._parse_docstring(
io.StringIO("""
""")
)
assert parsed == {}
def test_only_comment():
with open("tests/test_files/no_req.py") as f:
parsed = parsing._parse_docstring(f)
assert parsed == {}
def test_req_in_module_docstring_triple_doublequoute():
with open("tests/test_files/req_module.py") as f:
parsed = parsing._parse_docstring(f)
assert parsed == {REPO_PYPI: get_reqs("foo", "bar")}
def test_req_in_module_docstring_triple_singlequote():
with open("tests/test_files/req_module_2.py") as f:
parsed = parsing._parse_docstring(f)
assert parsed == {REPO_PYPI: get_reqs("foo", "bar")}
def test_req_in_module_docstring_one_doublequote():
with open("tests/test_files/req_module_3.py") as f:
parsed = parsing._parse_docstring(f)
assert parsed == {}
def test_req_in_class_docstring():
with open("tests/test_files/req_class.py") as f:
parsed = parsing._parse_docstring(f)
# no requirements found
assert parsed == {}
def test_req_in_def_docstring():
with open("tests/test_files/req_def.py") as f:
parsed = parsing._parse_docstring(f)
# no requirements found
assert parsed == {}
def test_req_in_multi_docstring():
with open("tests/test_files/req_all.py") as f:
parsed = parsing._parse_docstring(f)
# Only module requirements was found
assert parsed == {REPO_PYPI: get_reqs("foo==1.4")}
def test_fades_word_as_part_of_text():
with open("tests/test_files/fades_as_part_of_other_word.py") as f:
parsed = parsing._parse_docstring(f)
assert parsed == {}
def test_mixed_backends():
with open("tests/test_files/req_mixed_backends.py") as f:
parsed = parsing._parse_docstring(f)
# Only module requirements was found
assert parsed == {
REPO_PYPI: get_reqs("foo", "bar"),
REPO_VCS: [
parsing.VCSDependency("git+http://whatever"),
parsing.VCSDependency("anotherurl"),
],
}
fades-master/tests/test_multiplatform.py 0000664 0001750 0001750 00000005704 15215454131 020114 0 ustar tchet tchet # Copyright 2016-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General
# Public License version 3, as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.
# If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for the helpers in multiplatform."""
import threading
import time
import unittest
from pathlib import Path
from fades.multiplatform import filelock
class LockChecker(threading.Thread):
"""Helper to check the lock in other thread.
The time.sleep() in the middle of the process is for time.time()
granularity in different platforms to not mess our tests.
"""
def __init__(self, filepath):
self.filepath = filepath
self.in_lock = self.post_work = None
self.middle_work = threading.Event()
super().__init__()
def run(self):
with filelock(self.filepath):
time.sleep(.01)
self.in_lock = time.time()
self.middle_work.wait()
self.post_work = time.time()
class LockCacheTestCase(unittest.TestCase):
"""Tests for the locking utility."""
def setUp(self):
self.test_path = Path("test_filelock")
def tearDown(self):
if self.test_path.exists():
self.test_path.unlink()
def wait(self, lock_checker, attr_name):
"""Wait at most a second for the LockChecker to end."""
for i in range(10):
attr = getattr(lock_checker, attr_name)
if attr is not None:
# ended!
return
time.sleep(.3)
self.fail("LC didnt end: %s" % (lock_checker,))
def test_lock_alone(self):
lc = LockChecker(self.test_path)
lc.start()
lc.middle_work.set()
self.wait(lc, 'post_work')
def test_lock_intermixed(self):
lc1 = LockChecker(self.test_path)
lc1.start()
self.wait(lc1, 'in_lock')
lc2 = LockChecker(self.test_path)
lc2.start()
lc1.middle_work.set()
self.wait(lc1, 'post_work')
lc2.middle_work.set()
self.wait(lc2, 'post_work')
# check LC 2 waited to enter
self.assertGreater(lc2.in_lock, lc1.post_work)
def test_lock_exploding(self):
# get the lock and explode in the middle (then ignore the blast)
try:
with filelock(self.test_path):
raise ValueError("pumba")
except ValueError:
pass
# get the lock again
with filelock(self.test_path):
pass
fades-master/tests/test_main.py 0000664 0001750 0001750 00000030174 15215454131 016140 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for some code in main."""
import os
import unittest
from pathlib import Path
from unittest.mock import patch
from packaging.requirements import Requirement
from fades import VERSION, FadesError, __version__, main, parsing, REPO_PYPI, REPO_VCS
from tests import create_tempfile
class VirtualenvCheckingTestCase(unittest.TestCase):
"""Tests for the virtualenv checker."""
def test_have_realprefix(self):
resp = main.detect_inside_virtualenv('prefix', 'real_prefix', 'base_prefix')
self.assertTrue(resp)
def test_no_baseprefix(self):
resp = main.detect_inside_virtualenv('prefix', None, None)
self.assertFalse(resp)
def test_prefix_is_baseprefix(self):
resp = main.detect_inside_virtualenv('prefix', None, 'prefix')
self.assertFalse(resp)
def test_prefix_is_not_baseprefix(self):
resp = main.detect_inside_virtualenv('prefix', None, 'other prefix')
self.assertTrue(resp)
class DepsGatheringTestCase(unittest.TestCase):
"""Tests for the gathering stage of consolidate_dependencies."""
def test_needs_ipython(self):
d = main.consolidate_dependencies(needs_ipython=True, child_program=None,
requirement_files=None, manual_dependencies=None)
self.assertDictEqual(d, {'pypi': {Requirement('ipython')}})
def test_child_program(self):
child_program = 'tests/test_files/req_module.py'
d = main.consolidate_dependencies(needs_ipython=False, child_program=child_program,
requirement_files=None, manual_dependencies=None)
self.assertDictEqual(d, {'pypi': {Requirement('foo'), Requirement('bar')}})
def test_requirement_files(self):
requirement_files = [create_tempfile(self, ['dep'])]
d = main.consolidate_dependencies(needs_ipython=False, child_program=None,
requirement_files=requirement_files,
manual_dependencies=None)
self.assertDictEqual(d, {'pypi': {Requirement('dep')}})
def test_manual_dependencies(self):
manual_dependencies = ['dep']
d = main.consolidate_dependencies(needs_ipython=False, child_program=None,
requirement_files=None,
manual_dependencies=manual_dependencies)
self.assertDictEqual(d, {'pypi': {Requirement('dep')}})
class DepsMergingTestCase(unittest.TestCase):
"""Tests for the merging stage of consolidate_dependencies."""
def test_two_different(self):
requirement_files = [create_tempfile(self, ['1', '2'])]
manual_dependencies = ['vcs::3', 'vcs::4']
d = main.consolidate_dependencies(needs_ipython=False, child_program=None,
requirement_files=requirement_files,
manual_dependencies=manual_dependencies)
self.assertEqual(d, {
'pypi': {Requirement('1'), Requirement('2')},
'vcs': {parsing.VCSDependency('3'), parsing.VCSDependency('4')}
})
def test_two_same_repo(self):
requirement_files = [create_tempfile(self, ['1', '2'])]
manual_dependencies = ['3', '4']
d = main.consolidate_dependencies(needs_ipython=False, child_program=None,
requirement_files=requirement_files,
manual_dependencies=manual_dependencies)
self.assertDictEqual(d, {
'pypi': {Requirement('1'), Requirement('2'), Requirement('3'), Requirement('4')}
})
def test_complex_case(self):
child_program = create_tempfile(self, ['"""fades:', '1', '2', '"""'])
requirement_files = [create_tempfile(self, ['3', 'vcs::5'])]
manual_dependencies = ['vcs::4', 'vcs::6']
d = main.consolidate_dependencies(needs_ipython=False, child_program=child_program,
requirement_files=requirement_files,
manual_dependencies=manual_dependencies)
self.assertEqual(d, {
'pypi': {Requirement('1'), Requirement('2'), Requirement('3')},
'vcs': {parsing.VCSDependency('5'), parsing.VCSDependency('4'),
parsing.VCSDependency('6')}
})
def test_one_duplicated(self):
requirement_files = [create_tempfile(self, ['2', '2'])]
manual_dependencies = None
d = main.consolidate_dependencies(needs_ipython=False, child_program=None,
requirement_files=requirement_files,
manual_dependencies=manual_dependencies)
self.assertDictEqual(d, {
'pypi': {Requirement('2')}
})
def test_two_different_with_dups(self):
requirement_files = [create_tempfile(self, ['1', '2', '2', '2'])]
manual_dependencies = ['vcs::3', 'vcs::4', 'vcs::1', 'vcs::2']
d = main.consolidate_dependencies(needs_ipython=False, child_program=None,
requirement_files=requirement_files,
manual_dependencies=manual_dependencies)
self.assertEqual(d, {
'pypi': {Requirement('1'), Requirement('2')},
'vcs': {parsing.VCSDependency('1'), parsing.VCSDependency('2'),
parsing.VCSDependency('3'), parsing.VCSDependency('4')}
})
class MiscTestCase(unittest.TestCase):
"""Miscellaneous tests."""
def test_version_show(self):
self.assertEqual(
__version__,
'.'.join([str(v) for v in VERSION]),
)
class ChildProgramDeciderTestCase(unittest.TestCase):
"""Check how the child program is decided."""
def test_indicated_with_executable_flag(self):
analyzable, child = main.decide_child_program(True, False, "foobar.py")
self.assertIsNone(analyzable)
self.assertEqual(child, "foobar.py")
def test_no_child_at_all(self):
analyzable, child = main.decide_child_program(False, False, None)
self.assertIsNone(analyzable)
self.assertIsNone(child)
def test_normal_child_program(self):
child_path = create_tempfile(self, "")
analyzable, child = main.decide_child_program(False, False, child_path)
self.assertEqual(analyzable, Path(child_path))
self.assertEqual(child, child_path)
def test_normal_child_program_not_found(self):
with self.assertRaises(FadesError):
main.decide_child_program(False, False, 'does_not_exist.py')
def test_normal_child_program_no_access(self):
child_path = create_tempfile(self, "")
os.chmod(child_path, 333) # Remove read permission.
self.addCleanup(os.chmod, child_path, 644)
with self.assertRaises(FadesError):
main.decide_child_program(False, False, 'does_not_exist.py')
def test_remote_child_program_simple(self):
with patch('fades.helpers.download_remote_script') as mock:
mock.return_value = "new_path_script"
analyzable, child = main.decide_child_program(
False, False, "http://scripts.com/foobar.py")
mock.assert_called_with("http://scripts.com/foobar.py")
# check that analyzable and child are the same, and that its content is the remote one
self.assertEqual(analyzable, Path("new_path_script"))
self.assertEqual(child, "new_path_script")
def test_remote_child_program_ssl(self):
with patch('fades.helpers.download_remote_script') as mock:
mock.return_value = "new_path_script"
analyzable, child = main.decide_child_program(
False, False, "https://scripts.com/foobar.py")
mock.assert_called_with("https://scripts.com/foobar.py")
# check that analyzable and child are the same, and that its content is the remote one
self.assertEqual(analyzable, Path("new_path_script"))
self.assertEqual(child, "new_path_script")
def test_indicated_with_executable_flag_with_relative_path(self):
"""Relative paths not allowed when using --exec."""
with self.assertRaises(FadesError):
main.decide_child_program(True, False, os.path.join("path", "../foobar.py"))
def test_indicated_with_executable_flag_with_absolute_path(self):
"""Absolute paths are allowed when using --exec."""
analyzable, child = main.decide_child_program(True, False, "/tmp/foo/bar.py")
self.assertIsNone(analyzable)
self.assertEqual(child, "/tmp/foo/bar.py")
def test_module(self):
child_path = 'foo.bar'
analyzable, child = main.decide_child_program(False, True, child_path)
self.assertIsNone(analyzable)
self.assertEqual(child, child_path)
# ---------------------------------------
# autoimport tests
def _autoimport_safe_call(*args, **kwargs):
"""Call the tested function and always remove the tempfile after the test."""
fpath = main.get_autoimport_scriptname(*args, **kwargs)
with open(fpath, "rt", encoding='utf8') as fh:
content = fh.read()
os.unlink(fpath)
return content
def test_autoimport_simple():
"""Simplest autoimport call."""
dependencies = {
REPO_PYPI: {Requirement('mymod')},
}
content = _autoimport_safe_call(dependencies, is_ipython=False)
assert content.startswith(main.AUTOIMPORT_HEADER)
assert main.AUTOIMPORT_MOD_IMPORTER.format(module='mymod') in content
def test_autoimport_several_dependencies():
"""Indicate several dependencies."""
dependencies = {
REPO_PYPI: {Requirement('mymod1'), Requirement('mymod2')},
}
content = _autoimport_safe_call(dependencies, is_ipython=False)
assert content.startswith(main.AUTOIMPORT_HEADER)
assert main.AUTOIMPORT_MOD_IMPORTER.format(module='mymod1') in content
assert main.AUTOIMPORT_MOD_IMPORTER.format(module='mymod2') in content
def test_autoimport_including_ipython():
"""Call with ipython modifier."""
dependencies = {
REPO_PYPI: {
Requirement('mymod'),
Requirement('ipython'), # this one is automatically added
},
}
content = _autoimport_safe_call(dependencies, is_ipython=True)
assert main.AUTOIMPORT_HEADER not in content
assert main.AUTOIMPORT_MOD_IMPORTER.format(module='mymod') in content
assert 'ipython' not in content
def test_autoimport_no_pypi_dep():
"""Case with no pypi dependencies."""
dependencies = {
REPO_PYPI: {Requirement('my_pypi_mod')},
REPO_VCS: {'my_vcs_dependency'},
}
content = _autoimport_safe_call(dependencies, is_ipython=False)
assert main.AUTOIMPORT_MOD_IMPORTER.format(module='my_pypi_mod') in content
assert main.AUTOIMPORT_MOD_SKIPPING.format(dependency='my_vcs_dependency') in content
def test_autoimport_importer_mod_ok(capsys):
"""Check the generated code to import a module when works fine."""
code = main.AUTOIMPORT_MOD_IMPORTER.format(module='time') # something from stdlib, always ok
exec(code)
assert capsys.readouterr().out == "::fades:: automatically imported 'time'\n"
def test_autoimport_importer_mod_fail(capsys):
"""Check the generated code to import a module when works fine."""
code = main.AUTOIMPORT_MOD_IMPORTER.format(module='not_there_should_explode')
exec(code)
assert capsys.readouterr().out == "::fades:: FAILED to autoimport 'not_there_should_explode'\n"
fades-master/tests/test_logger.py 0000664 0001750 0001750 00000002647 15215454131 016477 0 ustar tchet tchet # Copyright 2018 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for logger related code."""
from fades.logger import set_up as log_set_up
def test_salutes_info(logs):
"""Check saluting handler."""
logger = log_set_up(verbose=False, quiet=True)
logger.warning("test foobar")
assert "Hi! This is fades" in logs.info
assert "test foobar" in logs.warning
def test_salutes_once(logs):
logger = log_set_up(verbose=False, quiet=False)
logger.info("test foobar")
assert "Hi! This is fades" in logs.info
assert "test foobar" in logs.info
# again, check this time it didn't salute, but original log message is ok
logs.reset()
logger.info("test barbarroja")
assert "Hi! This is fades" not in logs.info
assert "test barbarroja" in logs.info
fades-master/tests/test_infra.py 0000664 0001750 0001750 00000005430 15215454131 016310 0 ustar tchet tchet # Copyright 2017-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for infrastructure stuff."""
import io
import logging
from unittest.mock import patch
import docutils.core
import pydocstyle
import pytest
import rst2html5_
from flake8.api.legacy import get_style_guide
from pyuca import Collator
from tests import get_python_filepaths
FLAKE8_ROOTS = ['fades', 'tests']
FLAKE8_OPTIONS = {'max_line_length': 99, 'select': ['E', 'W', 'F', 'C', 'N']}
PEP257_ROOTS = ['fades']
# avoid seeing all DEBUG logs if the test fails
for logger_name in ('flake8.plugins', 'flake8.api', 'flake8.checker', 'flake8.main'):
logging.getLogger(logger_name).setLevel(logging.CRITICAL)
def test_flake8_pytest(capsys):
python_filepaths = get_python_filepaths(FLAKE8_ROOTS)
style_guide = get_style_guide(**FLAKE8_OPTIONS)
report = style_guide.check_files(python_filepaths)
if report.total_errors != 0:
out, _ = capsys.readouterr()
pytest.fail(f"There are {report.total_errors} issues!\n{''.join(out)}")
def test_pep257_pytest():
python_filepaths = get_python_filepaths(PEP257_ROOTS)
to_ignore = {
"D105", # Missing docstring in magic method
"D107", # Missing docstring in __init__
}
to_include = pydocstyle.violations.conventions.pep257 - to_ignore
result = list(pydocstyle.check(python_filepaths, select=to_include))
assert len(result) == 0, "There are issues!\n" + '\n'.join(map(str, result))
def test_readme_sanity():
fake_stdout = io.StringIO() # just to ignore the output
fake_stderr = io.StringIO() # will have content if there are problems
with open('README.rst', 'rt', encoding='utf8') as fh:
with patch('sys.stdout', fake_stdout):
with patch('sys.stderr', fake_stderr):
docutils.core.publish_file(source=fh, writer=rst2html5_.HTML5Writer())
errors = fake_stderr.getvalue()
assert not bool(errors), "There are issues!\n" + errors
def test_authors_ordering():
with open('AUTHORS', 'rt', encoding='utf8') as fh:
authors = fh.readlines()
ordered_authors = sorted(authors, key=Collator().sort_key)
assert authors == ordered_authors
fades-master/tests/test_helpers.py 0000664 0001750 0001750 00000061632 15215454131 016661 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for functions in helpers."""
import io
import json
import os
import sys
import tempfile
import unittest
from http.server import HTTPStatus
from pathlib import Path
from unittest.mock import patch
from urllib.error import HTTPError
from urllib.request import Request
import logassert
import pytest
from xdg import BaseDirectory
from fades import helpers, parsing
PATH_TO_EXAMPLES = "tests/examples/"
class GetInterpreterVersionTestCase(unittest.TestCase):
"""Some tests for get_interpreter_version."""
def test_current_version(self):
values = {None: ('/path/to/python1.0'), "/path/to/python": ('/path/to/python1.0')}
def side_effect(arg=None):
return values[arg]
with patch.object(helpers, '_get_interpreter_info') as mock:
mock.side_effect = side_effect
interpreter, is_current = helpers.get_interpreter_version('/path/to/python')
self.assertEqual(is_current, True)
def test_other_version(self):
values = {None: ('/path/to/python1.0'), "/path/to/python": ('/path/to/python9.8')}
def side_effect(arg=None):
return values[arg]
with patch.object(helpers, '_get_interpreter_info') as mock:
mock.side_effect = side_effect
interpreter, is_current = helpers.get_interpreter_version('/path/to/python')
self.assertEqual(is_current, False)
def test_none_requested(self):
values = {None: ('/path/to/python1.0'), "/path/to/python": ('/path/to/python9.8')}
def side_effect(arg=None):
return values[arg]
with patch.object(helpers, '_get_interpreter_info') as mock:
mock.side_effect = side_effect
interpreter, is_current = helpers.get_interpreter_version(requested_interpreter=None)
self.assertEqual(is_current, True)
self.assertTrue(mock.call_count, 1)
class GetInterpreterInfoTestCase(unittest.TestCase):
"""Some tests for _get_interpreter_info."""
def setUp(self):
logassert.setup(self, 'fades.helpers')
def test_none_requested(self):
with patch.object(sys, 'version_info', (9, 8)), patch.object(sys,
'executable',
'/path/to/python'):
interpreter = helpers._get_interpreter_info(None)
self.assertEqual(interpreter, '/path/to/python9.8')
def test_requested_fullpath_nodigit(self):
response = [('{"serial": 0,"path": "/path/to/python","minor": 8,"major": 9,"micro": 0,'
'"releaselevel": "ultimate"}')]
with patch.object(helpers, 'logged_exec', return_value=response):
interpreter = helpers._get_interpreter_info('/path/to/python')
self.assertEqual(interpreter, '/path/to/python9.8')
def test_requested_fullpath_with_major(self):
response = [('{"serial": 0,"path": "/path/to/python9","minor": 8,"major": 9,"micro": 0,'
'"releaselevel": "ultimate"}')]
with patch.object(helpers, 'logged_exec', return_value=response):
interpreter = helpers._get_interpreter_info('/path/to/python9')
self.assertEqual(interpreter, '/path/to/python9.8')
def test_requested_fullpath_with_minor(self):
response = [('{"serial": 0,"path": "/path/to/python9.8","minor": 8,"major": 9,"micro": 0,'
'"releaselevel": "ultimate"}')]
with patch.object(helpers, 'logged_exec', return_value=response):
interpreter = helpers._get_interpreter_info('/path/to/python9.8')
self.assertEqual(interpreter, '/path/to/python9.8')
def test_requested_nodigit(self):
response = [('{"serial": 0,"path": "/path/to/python","minor": 8,"major": 9,"micro": 0,'
'"releaselevel": "ultimate"}')]
with patch.object(helpers, 'logged_exec', return_value=response):
interpreter = helpers._get_interpreter_info('python')
self.assertEqual(interpreter, '/path/to/python9.8')
def test_requested_with_major(self):
response = [('{"serial": 0,"path": "/path/to/python9","minor": 8,"major": 9,"micro": 0,'
'"releaselevel": "ultimate"}')]
with patch.object(helpers, 'logged_exec', return_value=response):
interpreter = helpers._get_interpreter_info('python9')
self.assertEqual(interpreter, '/path/to/python9.8')
def test_requested_with_minor(self):
response = [('{"serial": 0,"path": "/path/to/python9.8","minor": 8,"major": 9,"micro": 0,'
'"releaselevel": "ultimate"}')]
with patch.object(helpers, 'logged_exec', return_value=response):
interpreter = helpers._get_interpreter_info('python9.8')
self.assertEqual(interpreter, '/path/to/python9.8')
def test_requested_not_exists(self):
side_effect = IOError("[Errno 2] No such file or directory: 'pythonME'")
with patch('fades.helpers.logged_exec') as mock_lexec:
mock_lexec.side_effect = side_effect
with self.assertRaises(Exception):
helpers._get_interpreter_info('pythonME')
self.assertLoggedError("Error getting requested interpreter version:"
" [Errno 2] No such file or directory: 'pythonME'")
class GetLatestVersionNumberTestCase(unittest.TestCase):
"""Some tests for get_latest_version_number."""
def setUp(self):
logassert.setup(self, 'fades.helpers')
def test_get_version_correct(self):
with open(os.path.join(PATH_TO_EXAMPLES, 'pypi_get_version_ok.json'), "rb") as fh:
with patch('urllib.request.urlopen') as mock_urlopen:
mock_urlopen.return_value = fh
last_version = helpers.get_latest_version_number("some_package")
mock_urlopen.assert_called_once_with(helpers.BASE_PYPI_URL.format(name="some_package"))
self.assertEqual(last_version, '2.8.1')
def test_get_version_wrong(self):
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.side_effect = HTTPError("url",
500,
"mgs",
{},
io.BytesIO())
mock_urlopen.return_value = mock_http_response
self.assertRaises(Exception, helpers.get_latest_version_number, "some_package")
self.assertLoggedWarning("Network error.")
def test_get_version_fail(self):
with open(os.path.join(PATH_TO_EXAMPLES, 'pypi_get_version_fail.json'), "rb") as fh:
with patch('urllib.request.urlopen') as mock_urlopen:
mock_urlopen.return_value = fh
self.assertRaises(KeyError, helpers.get_latest_version_number, "some_package")
self.assertLoggedError("Could not get the version of the package. Error:")
class CheckPyPIUpdatesTestCase(unittest.TestCase):
"""Some tests for check_pypi_updates."""
def setUp(self):
logassert.setup(self, 'fades.helpers')
def test_check_pypi_updates_with_and_without_version(self):
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.side_effect = [b'{"info": {"version": "1.9"}}',
b'{"info": {"version": "2.1"}}']
mock_urlopen.return_value = mock_http_response
requested = parsing.parse_manual(["django==1.7.5", "requests"])
dependencies = helpers.check_pypi_updates(requested)
dep_django = dependencies['pypi'][0]
dep_request = dependencies['pypi'][1]
self.assertLoggedInfo('There is a new version of django: 1.9')
self.assertEqual(str(dep_request.specifier), "==2.1")
self.assertEqual(str(dep_django.specifier), "==1.7.5")
self.assertLoggedInfo("The latest version of 'requests' is 2.1 and will use it.")
def test_check_pypi_updates_with_a_higher_version_of_a_package_simple(self):
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.side_effect = [b'{"info": {"version": "1.9"}}']
mock_urlopen.return_value = mock_http_response
helpers.check_pypi_updates(parsing.parse_manual(["django==100.1.1"]))
self.assertLoggedWarning(
"The requested version for django is greater than latest found in PyPI: 1.9")
def test_check_pypi_updates_with_a_higher_version_of_a_package_real_order(self):
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.side_effect = [b'{"info": {"version": "2.9"}}']
mock_urlopen.return_value = mock_http_response
helpers.check_pypi_updates(parsing.parse_manual(["django==10.1"]))
self.assertLoggedWarning(
"The requested version for django is greater than latest found in PyPI: 2.9")
def test_check_pypi_updates_with_the_latest_version_of_a_package(self):
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.side_effect = [b'{"info": {"version": "1.9"}}']
mock_urlopen.return_value = mock_http_response
helpers.check_pypi_updates(parsing.parse_manual(["django==1.9"]))
self.assertLoggedInfo(
"The requested version for django is the latest one in PyPI: 1.9")
class GetDirsTestCase(unittest.TestCase):
"""Utilities to get dir."""
_home = os.path.expanduser("~")
def test_basedir_xdg(self):
direct = helpers.get_basedir()
self.assertEqual(direct, Path(BaseDirectory.xdg_data_home) / 'fades')
def _fake_snap_env_dir(self, direct):
"""Fake Snap's environment variable."""
os.environ[helpers.SNAP_BASEDIR_NAME] = direct
self.addCleanup(os.environ.pop, helpers.SNAP_BASEDIR_NAME)
def test_basedir_snap(self):
with tempfile.TemporaryDirectory() as dirname:
self._fake_snap_env_dir(dirname)
direct = helpers.get_basedir()
self.assertEqual(direct, Path(dirname) / 'data')
def test_basedir_default(self):
with patch.object(helpers, "_get_basedirectory") as mock:
mock.side_effect = ImportError()
direct = helpers.get_basedir()
self.assertEqual(direct, Path(self._home) / '.fades')
def test_basedir_xdg_nonexistant(self):
with patch("xdg.BaseDirectory") as mock:
with tempfile.TemporaryDirectory() as dirname:
mock.xdg_data_home = dirname
direct = helpers.get_basedir()
self.assertTrue(os.path.exists(direct))
def test_basedir_snap_nonexistant(self):
with tempfile.TemporaryDirectory() as dirname:
self._fake_snap_env_dir(dirname)
direct = helpers.get_basedir()
self.assertTrue(os.path.exists(direct))
def test_confdir_xdg(self):
direct = helpers.get_confdir()
self.assertEqual(direct, Path(BaseDirectory.xdg_config_home) / 'fades')
def test_confdir_snap(self):
with tempfile.TemporaryDirectory() as dirname:
self._fake_snap_env_dir(dirname)
direct = helpers.get_confdir()
self.assertEqual(direct, Path(dirname) / 'config')
def test_confdir_default(self):
with patch.object(helpers, "_get_basedirectory") as mock:
mock.side_effect = ImportError()
direct = helpers.get_confdir()
self.assertEqual(direct, Path(self._home) / '.fades')
def test_confdir_xdg_nonexistant(self):
with patch("xdg.BaseDirectory") as mock:
with tempfile.TemporaryDirectory() as dirname:
mock.xdg_config_home = dirname
direct = helpers.get_confdir()
self.assertTrue(os.path.exists(direct))
def test_confdir_snap_nonexistant(self):
with tempfile.TemporaryDirectory() as dirname:
self._fake_snap_env_dir(dirname)
direct = helpers.get_confdir()
self.assertTrue(os.path.exists(direct))
class CheckPackageExistenceTestCase(unittest.TestCase):
"""Test for check_pypi_exists."""
def setUp(self):
logassert.setup(self, 'fades.helpers')
def test_exists(self):
deps = parsing.parse_manual(["foo"])
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.status = HTTPStatus.OK
mock_urlopen.return_value = mock_http_response
exists = helpers.check_pypi_exists(deps)
self.assertTrue(exists)
self.assertLogged("exists in PyPI")
def test_all_exists(self):
dependencies = parsing.parse_manual(['foo', 'bar', 'baz'])
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.status = HTTPStatus.OK
mock_urlopen.side_effect = [mock_http_response] * 3
exists = helpers.check_pypi_exists(dependencies)
self.assertTrue(exists)
self.assertLogged("exists in PyPI")
def test_doesnt_exists(self):
dependency = parsing.parse_manual(["foo"])
with patch('urllib.request.urlopen') as mock_urlopen:
mock_http_error = HTTPError("url", HTTPStatus.NOT_FOUND, "mgs", {}, io.BytesIO())
mock_urlopen.side_effect = mock_http_error
exists = helpers.check_pypi_exists(dependency)
self.assertFalse(exists)
self.assertLoggedError("foo doesn't exists in PyPI.")
def test_one_doesnt_exists(self):
dependencies = parsing.parse_manual(["foo", "bar"])
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_error = HTTPError("url", HTTPStatus.NOT_FOUND, "mgs", {}, io.BytesIO())
mock_http_response.status = HTTPStatus.OK
mock_urlopen.side_effect = [mock_http_response, mock_http_error]
exists = helpers.check_pypi_exists(dependencies)
self.assertFalse(exists)
self.assertLoggedError("bar doesn't exists in PyPI.")
def test_error_hitting_pypi(self):
dependency = parsing.parse_manual(["foo"])
with self.assertRaises(Exception):
with patch('urllib.request.urlopen') as mock_urlopen:
mock_urlopen.side_effect = ValueError("cabum!!")
helpers.check_pypi_exists(dependency)
def test_status_code_error(self):
dependency = parsing.parse_manual(["foo"])
with self.assertRaises(Exception):
with patch('urllib.request.urlopen') as mock_urlopen:
mock_http_error = HTTPError("url", 400, "mgs", {}, io.BytesIO())
mock_urlopen.side_effect = mock_http_error
helpers.check_pypi_exists(dependency)
def test_redirect_response(self):
deps = parsing.parse_manual(["foo"])
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.status = 302 # redirect
mock_urlopen.return_value = mock_http_response
exists = helpers.check_pypi_exists(deps)
self.assertTrue(exists)
self.assertLoggedWarning("Got a (unexpected) HTTP_STATUS")
class ScriptDownloaderTestCase(unittest.TestCase):
"""Check the script downloader."""
def setUp(self):
logassert.setup(self, 'fades.helpers')
def test_external_public_function(self):
test_url = "http://scripts.com/foobar.py"
test_content = "test content of the remote script ññ"
with patch('fades.helpers._ScriptDownloader') as mock_downloader_class:
mock_downloader = mock_downloader_class()
mock_downloader.get.return_value = test_content
mock_downloader.name = 'mock downloader'
filepath = helpers.download_remote_script(test_url)
# plan to remove the downloaded content (so test remains clean)
self.addCleanup(os.unlink, filepath)
# checks
mock_downloader_class.assert_called_with(test_url)
self.assertLoggedInfo(
"Downloading remote script from {!r}".format(test_url),
str(filepath), "(using 'mock downloader' downloader)")
with open(filepath, "rt", encoding='utf8') as fh:
self.assertEqual(fh.read(), test_content)
def test_decide_linkode(self):
url = "http://linkode.org/#02c5nESQBLEjgBRhUwJK74"
downloader = helpers._ScriptDownloader(url)
name = downloader._decide()
self.assertEqual(name, 'linkode')
def test_decide_pastebin(self):
url = "https://pastebin.com/sZGwz7SL"
downloader = helpers._ScriptDownloader(url)
name = downloader._decide()
self.assertEqual(name, 'pastebin')
def test_decide_gist(self):
url = "https://gist.github.com/facundobatista/6ff4f75760a9acc35e68bae8c1d7da1c"
downloader = helpers._ScriptDownloader(url)
name = downloader._decide()
self.assertEqual(name, 'gist')
def test_downloader_raw(self):
test_url = "http://scripts.com/foobar.py"
raw_service_response = b"test content of the remote script"
downloader = helpers._ScriptDownloader(test_url)
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.return_value = raw_service_response
mock_urlopen.return_value = mock_http_response
mock_http_response.geturl.return_value = test_url
content = downloader.get()
# check urlopen was called with the proper url, and passing correct headers
headers = {
'Accept': 'text/plain',
'User-agent': helpers._ScriptDownloader.USER_AGENT,
}
(call,) = mock_urlopen.mock_calls
(called_request,) = call[1]
self.assertIsInstance(called_request, Request)
self.assertEqual(called_request.full_url, test_url)
self.assertEqual(called_request.headers, headers)
self.assertEqual(content, raw_service_response.decode("utf8"))
def test_downloader_linkode(self):
test_url = "http://linkode.org/#02c5nESQBLEjgBRhUwJK74"
test_content = "test content of the remote script áéÃóú"
raw_service_response = json.dumps({
'content': test_content,
'morestuff': 'whocares',
}).encode("utf8")
downloader = helpers._ScriptDownloader(test_url)
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.return_value = raw_service_response
mock_urlopen.return_value = mock_http_response
content = downloader.get()
# check urlopen was called with the proper url, and passing correct headers
headers = {
'Accept': 'application/json',
'User-agent': helpers._ScriptDownloader.USER_AGENT,
}
(call,) = mock_urlopen.mock_calls
(called_request,) = call[1]
self.assertIsInstance(called_request, Request)
self.assertEqual(
called_request.full_url, "https://linkode.org/api/1/linkodes/02c5nESQBLEjgBRhUwJK74")
self.assertEqual(called_request.headers, headers)
self.assertEqual(content, test_content)
def test_downloader_pastebin(self):
test_url = "http://pastebin.com/sZGwz7SL"
real_url = "https://pastebin.com/raw/sZGwz7SL"
test_content = "test content of the remote script áéÃóú"
raw_service_response = test_content.encode("utf8")
downloader = helpers._ScriptDownloader(test_url)
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.return_value = raw_service_response
mock_urlopen.return_value = mock_http_response
mock_http_response.geturl.return_value = real_url
content = downloader.get()
# check urlopen was called with the proper url, and passing correct headers
headers = {
'Accept': 'text/plain',
'User-agent': helpers._ScriptDownloader.USER_AGENT,
}
(call,) = mock_urlopen.mock_calls
(called_request,) = call[1]
self.assertIsInstance(called_request, Request)
self.assertEqual(called_request.full_url, real_url)
self.assertEqual(called_request.headers, headers)
self.assertEqual(content, test_content)
def test_downloader_gist(self):
test_url = "http://gist.github.com/facundobatista/6ff4f75760a9acc35e68bae8c1d7da1c"
real_url = "https://gist.github.com/facundobatista/6ff4f75760a9acc35e68bae8c1d7da1c/raw"
test_content = "test content of the remote script áéÃóú"
raw_service_response = test_content.encode("utf8")
downloader = helpers._ScriptDownloader(test_url)
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.return_value = raw_service_response
mock_urlopen.return_value = mock_http_response
mock_http_response.geturl.return_value = real_url
content = downloader.get()
# check urlopen was called with the proper url, and passing correct headers
headers = {
'Accept': 'text/plain',
'User-agent': helpers._ScriptDownloader.USER_AGENT,
}
(call,) = mock_urlopen.mock_calls
(called_request,) = call[1]
self.assertIsInstance(called_request, Request)
self.assertEqual(called_request.full_url, real_url)
self.assertEqual(called_request.headers, headers)
self.assertEqual(content, test_content)
def test_downloader_raw_with_redirection(self):
test_url = "http://bit.ly/will-redirect"
final_url = "http://real-service.com/"
raw_service_response = b"test content of the remote script"
downloader = helpers._ScriptDownloader(test_url)
response_contents = [
b"whatever; we don't care as we are redirectect",
raw_service_response,
]
with patch('urllib.request.urlopen') as mock_urlopen:
with patch('http.client.HTTPResponse') as mock_http_response:
mock_http_response.read.side_effect = lambda: response_contents.pop()
mock_http_response.geturl.return_value = final_url
mock_urlopen.return_value = mock_http_response
content = downloader.get()
# two calls, first to the service that will redirect us, second to the final one
call1, call2 = mock_urlopen.mock_calls
(called_request,) = call1[1]
self.assertEqual(called_request.full_url, test_url)
(called_request,) = call2[1]
self.assertEqual(called_request.full_url, final_url)
self.assertEqual(content, raw_service_response.decode("utf8"))
self.assertLoggedInfo("Download redirect detect, now downloading from", final_url)
def test_getbinpath_posix(tmp_path):
realbin = tmp_path / "bin"
realbin.mkdir()
path = helpers.get_env_bin_path(tmp_path)
assert path == realbin
def test_getbinpath_windows(tmp_path):
realbin = tmp_path / "Scripts"
realbin.mkdir()
path = helpers.get_env_bin_path(tmp_path)
assert path == realbin
def test_getbinpath_missing(tmp_path):
with pytest.raises(ValueError):
helpers.get_env_bin_path(tmp_path)
fades-master/tests/test_files/ 0000775 0001750 0001750 00000000000 15215454131 015737 5 ustar tchet tchet fades-master/tests/test_files/req_module_3.py 0000664 0001750 0001750 00000000617 15215454131 020673 0 ustar tchet tchet # Copyright 2014 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
"Extended class from EnvBuilder to create a venv using a uuid4 id."
class FooClass():
"""Create always a virtualenv."""
def foo(self):
pass
fades-master/tests/test_files/req_module_2.py 0000664 0001750 0001750 00000001155 15215454131 020670 0 ustar tchet tchet # Copyright 2014 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
'''
Extended class from EnvBuilder to create a venv using a uuid4 id.
NOTE: this class only work in the same python version that Fades is
running. So, you don't need to have installed a virtualenv tool. For
other python versions Fades needs a virtualenv tool installed.
fades:
foo
bar
'''
class FooClass():
"""Create always a virtualenv."""
def foo(self):
pass
fades-master/tests/test_files/req_module.py 0000664 0001750 0001750 00000001155 15215454131 020447 0 ustar tchet tchet # Copyright 2014 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
"""
Extended class from EnvBuilder to create a venv using a uuid4 id.
NOTE: this class only work in the same python version that Fades is
running. So, you don't need to have installed a virtualenv tool. For
other python versions Fades needs a virtualenv tool installed.
fades:
foo
bar
"""
class FooClass():
"""Create always a virtualenv."""
def foo(self):
pass
fades-master/tests/test_files/req_mixed_backends.py 0000664 0001750 0001750 00000000140 15215454131 022113 0 ustar tchet tchet """
Bleh, groovy.
fades:
foo
pypi::bar
git+http://whatever
vcs::anotherurl
"""
fades-master/tests/test_files/req_def.py 0000664 0001750 0001750 00000000266 15215454131 017722 0 ustar tchet tchet import logging
logger = logging.getLogger(__name__)
def def_function():
"""Something.
requirements for fades:
foo
bar
!fades
More info...
"""
pass
fades-master/tests/test_files/req_class.py 0000664 0001750 0001750 00000001213 15215454131 020262 0 ustar tchet tchet # Copyright 2014 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
"""
Extended class from EnvBuilder to create a venv using a uuid4 id.
NOTE: this class only work in the same python version that Fades is
running. So, you don't need to have installed a virtualenv tool. For
other python versions Fades needs a virtualenv tool installed.
"""
class FooClass():
"""Foo class docstring.
requirements for fades:
foo
bar
"""
def __init__(self):
pass
fades-master/tests/test_files/req_all.py 0000664 0001750 0001750 00000001520 15215454131 017726 0 ustar tchet tchet # Copyright 2014 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
"""
Extended class from EnvBuilder to create a venv using a uuid4 id.
NOTE: this class only work in the same python version that Fades is
running. So, you don't need to have installed a virtualenv tool. For
other python versions Fades needs a virtualenv tool installed.
fades:
foo==1.4
"""
class FooClass():
"""Create always a virtualenv.
requirements for fades:
bar>1.8.9
"""
def __init__(self):
pass
def create(self, interpreter):
"""
Create a virtualenv using the virtualenv lib.
fades:
more>1.6
"""
print("create")
fades-master/tests/test_files/no_req.py 0000664 0001750 0001750 00000001136 15215454131 017575 0 ustar tchet tchet # Copyright 2014 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
"""
Extended class from EnvBuilder to create a venv using a uuid4 id.
NOTE: this class only work in the same python version that Fades is
running. So, you don't need to have installed a virtualenv tool. For
other python versions Fades needs a virtualenv tool installed.
"""
class FooClass():
"""Create always a virtualenv."""
def foo(self):
pass
fades-master/tests/test_files/fades_as_part_of_other_word.py 0000664 0001750 0001750 00000000273 15215454131 024026 0 ustar tchet tchet import logging
logger = logging.getLogger(__name__)
def def_function():
"""
the fades sweetly flower.
foo==1.4
No te enfades con python3!
bar>1.9
"""
pass
fades-master/tests/test_file_options.py 0000664 0001750 0001750 00000014433 15215454131 017706 0 ustar tchet tchet # Copyright 2016 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for file_options."""
import argparse
import unittest
from configparser import ConfigParser
from unittest.mock import patch
from fades import file_options
class OptionsFileTestCase(unittest.TestCase):
"""Check file_options.options_from_file()."""
def setUp(self):
self.argparser = argparse.ArgumentParser()
self.argparser.add_argument
self.argparser.add_argument('-f', '--foo', action='store_true')
self.argparser.add_argument('-b', '--bar', action='store')
self.argparser.add_argument('-d', '--dependency', action='append')
self.argparser.add_argument('positional', nargs='?', default=None)
def build_parser(self, args):
config_parser = ConfigParser()
config_parser['fades'] = args
return config_parser
@patch("fades.file_options.CONFIG_FILES", ('/foo/none', '/dev/null'))
def test_no_config_files(self):
args = self.argparser.parse_args([])
result = file_options.options_from_file(args)
self.assertEqual(args, result)
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini',))
@patch("configparser.ConfigParser.items")
def test_single_config_file_no_cli(self, mocked_parser):
mocked_parser.return_value = [('foo', 'true'), ('bar', 'hux')]
args = self.argparser.parse_args(['positional'])
result = file_options.options_from_file(args)
self.assertTrue(result.foo)
self.assertEqual(result.bar, 'hux')
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini',))
@patch("configparser.ConfigParser.items")
def test_single_config_file_with_cli(self, mocked_parser):
mocked_parser.return_value = [('foo', 'false'), ('bar', 'hux'), ('no_in_cli', 'testing')]
args = self.argparser.parse_args(['--foo', '--bar', 'other', 'positional'])
result = file_options.options_from_file(args)
self.assertTrue(result.foo)
self.assertEqual(result.bar, 'other')
self.assertEqual(result.no_in_cli, 'testing')
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini',))
@patch("configparser.ConfigParser.items")
def test_single_config_file_with_mergeable(self, mocked_parser):
mocked_parser.return_value = [('dependency', 'two')]
args = self.argparser.parse_args(
['--foo', '--bar', 'other', '--dependency', 'one', 'positional'])
result = file_options.options_from_file(args)
self.assertTrue(result.foo)
self.assertEqual(result.bar, 'other')
self.assertEqual(result.dependency, ['one', 'two'])
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini',))
@patch("configparser.ConfigParser.items")
def test_single_config_file_complex_mergeable(self, mocked_parser):
mocked_parser.return_value = [('dependency', 'requests>=2.1,<2.8,!=2.6.5')]
args = self.argparser.parse_args(
['--foo', '--bar', 'other', '--dependency', 'one', 'positional'])
result = file_options.options_from_file(args)
self.assertTrue(result.foo)
self.assertEqual(result.bar, 'other')
self.assertEqual(result.dependency, ['one', 'requests>=2.1,<2.8,!=2.6.5'])
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini', 'mock2.ini'))
@patch("configparser.ConfigParser.items")
def test_two_config_file_with_mergeable(self, mocked_parser):
mocked_parser.side_effect = [
[('dependency', 'two')],
[('dependency', 'three')],
]
args = self.argparser.parse_args(
['--foo', '--bar', 'other', '--dependency', 'one', 'positional'])
result = file_options.options_from_file(args)
self.assertTrue(result.foo)
self.assertEqual(result.bar, 'other')
self.assertEqual(result.dependency, ['one', 'two', 'three'])
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini', 'mock2.ini'))
@patch("configparser.ConfigParser.items")
def test_two_config_file_with_booleans(self, mocked_parser):
mocked_parser.side_effect = [
[('foo', 'true')],
[('foo', 'false')],
]
args = self.argparser.parse_args([])
result = file_options.options_from_file(args)
self.assertFalse(result.foo)
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini', 'mock2.ini'))
@patch("configparser.ConfigParser.items")
def test_two_config_file_override_by_cli(self, mocked_parser):
mocked_parser.side_effect = [
[('bar', 'no_this')],
[('bar', 'no_this_b')],
]
args = self.argparser.parse_args(['--bar', 'this'])
result = file_options.options_from_file(args)
self.assertEqual(result.bar, 'this')
self.assertIsInstance(args, argparse.Namespace)
@patch("fades.file_options.CONFIG_FILES", ('mock.ini', 'mock2.ini', 'mock3.ini'))
@patch("configparser.ConfigParser.items")
def test_three_config_file_override(self, mocked_parser):
mocked_parser.side_effect = [
[('bar', 'no_this')],
[('bar', 'neither_this')],
[('bar', 'this')],
]
args = self.argparser.parse_args([])
result = file_options.options_from_file(args)
self.assertEqual(result.bar, 'this')
self.assertIsInstance(args, argparse.Namespace)
fades-master/tests/test_envbuilder.py 0000664 0001750 0001750 00000040204 15215454131 017346 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Tests for the venv builder module."""
import os
import shutil
import tempfile
import unittest
from datetime import datetime, timedelta
from packaging.requirements import Requirement
from pathlib import Path
from unittest.mock import Mock, patch, call
import logassert
from fades import FadesError, REPO_PYPI, REPO_VCS
from fades import cache, envbuilder, parsing
from venv import EnvBuilder
def get_req(text):
"""Transform a text requirement into the Requirement object."""
return Requirement(text)
class EnvCreationTestCase(unittest.TestCase):
"""Check all the new venv creation."""
class FakeManager:
"""A fake repo manager."""
def __init__(self):
self.req_installed = []
self.really_installed = {}
def install(self, dependency):
self.req_installed.append(dependency)
def get_version(self, dependency):
return self.really_installed[dependency]
class FailInstallManager(FakeManager):
def install(self, dependency):
raise Exception("Kapow!")
def setUp(self):
logassert.setup(self, 'fades.envbuilder')
def test_create_simple(self):
requested = {
REPO_PYPI: [get_req('dep1 == v1'), get_req('dep2 == v2')]
}
interpreter = 'python3'
is_current = True
avoid_pip_upgrade = False
options = {"venv_options": []}
pip_options = []
with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
mock_create.return_value = ('env_path', 'env_bin_path', 'pip_installed')
mock_mgr_c.return_value = fake_manager = self.FakeManager()
fake_manager.really_installed = {'dep1': 'v1', 'dep2': 'v2'}
venv_data, installed = envbuilder.create_venv(
requested, interpreter, is_current, options, pip_options, avoid_pip_upgrade)
self.assertEqual(venv_data, {
'env_bin_path': 'env_bin_path',
'env_path': 'env_path',
'pip_installed': 'pip_installed',
})
self.assertDictEqual(installed, {
REPO_PYPI: {
'dep1': 'v1',
'dep2': 'v2',
}
})
expected_pipmanager_call = call(
'env_bin_path', pip_installed='pip_installed', options=[],
avoid_pip_upgrade=avoid_pip_upgrade)
self.assertEqual(mock_mgr_c.call_args, expected_pipmanager_call)
def test_create_vcs(self):
requested = {
REPO_VCS: [parsing.VCSDependency("someurl")]
}
interpreter = 'python3'
is_current = True
avoid_pip_upgrade = False
options = {"venv_options": []}
pip_options = []
with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
mock_create.return_value = ('env_path', 'env_bin_path', 'pip_installed')
mock_mgr_c.return_value = self.FakeManager()
venv_data, installed = envbuilder.create_venv(
requested, interpreter, is_current, options, pip_options, avoid_pip_upgrade)
self.assertEqual(venv_data, {
'env_bin_path': 'env_bin_path',
'env_path': 'env_path',
'pip_installed': 'pip_installed',
})
self.assertDictEqual(installed, {REPO_VCS: {'someurl': None}})
def test_unknown_repo(self):
requested = {
'unknown': {'dep': ''}
}
interpreter = 'python3'
is_current = True
avoid_pip_upgrade = False
options = {"venv_options": []}
pip_options = []
with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
mock_create.return_value = ('env_path', 'env_bin_path', 'pip_installed')
mock_mgr_c.return_value = self.FakeManager()
envbuilder.create_venv(
requested, interpreter, is_current, options, pip_options, avoid_pip_upgrade)
self.assertLoggedWarning("Install from 'unknown' not implemented")
def test_non_existing_dep(self):
requested = {
REPO_PYPI: [get_req('dep1 == 1000')]
}
interpreter = 'python3'
is_current = True
avoid_pip_upgrade = False
options = {'venv_options': []}
pip_options = []
with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
mock_create.return_value = ('env_path', 'env_bin_path', 'pip_installed')
mock_mgr_c.return_value = self.FailInstallManager()
with patch.object(envbuilder, 'destroy_venv', spec=True) as mock_destroy:
with self.assertRaises(FadesError) as cm:
envbuilder.create_venv(
requested, interpreter, is_current, options, pip_options,
avoid_pip_upgrade)
self.assertEqual(str(cm.exception), 'Dependency installation failed')
mock_destroy.assert_called_once_with('env_path')
self.assertLoggedDebug("Installation Step failed, removing virtual environment")
def test_different_versions(self):
requested = {
REPO_PYPI: [get_req('dep1 == v1'), get_req('dep2 == v2')]
}
interpreter = 'python3'
is_current = True
avoid_pip_upgrade = False
options = {"venv_options": []}
pip_options = []
with patch.object(envbuilder._FadesEnvBuilder, 'create_env') as mock_create:
with patch.object(envbuilder, 'PipManager') as mock_mgr_c:
mock_create.return_value = ('env_path', 'env_bin_path', 'pip_installed')
mock_mgr_c.return_value = fake_manager = self.FakeManager()
fake_manager.really_installed = {'dep1': 'vX', 'dep2': 'v2'}
_, installed = envbuilder.create_venv(
requested, interpreter, is_current, options, pip_options, avoid_pip_upgrade)
self.assertEqual(installed, {
REPO_PYPI: {
'dep1': 'vX',
'dep2': 'v2',
}
})
def test_create_system_site_pkgs_venv(self):
env_builder = envbuilder._FadesEnvBuilder()
interpreter = 'python3'
is_current = True
options = {"venv_options": ['--system-site-packages']}
with patch.object(EnvBuilder, 'create') as mock_create:
env_builder.create_env(interpreter, is_current, options)
self.assertTrue(env_builder.system_site_packages)
self.assertTrue(mock_create.called)
def test_create_pyvenv(self):
env_builder = envbuilder._FadesEnvBuilder()
interpreter = 'python3'
is_current = True
options = {"venv_options": []}
with patch.object(EnvBuilder, 'create') as mock_create:
env_builder.create_env(interpreter, is_current, options)
self.assertFalse(env_builder.system_site_packages)
self.assertTrue(mock_create.called)
def test_create_virtual_environment(self):
env_builder = envbuilder._FadesEnvBuilder()
interpreter = 'pythonX.Y'
is_current = False
options = {"venv_options": []}
with patch.object(envbuilder._FadesEnvBuilder, 'create_with_external_venv') as mock_create:
env_builder.create_env(interpreter, is_current, options)
mock_create.assert_called_with(interpreter, options['venv_options'])
class EnvDestructionTestCase(unittest.TestCase):
def test_destroy_venv(self):
builder = envbuilder._FadesEnvBuilder()
# make sure the virtualenv exists on disk
options = {"venv_options": [], "pip-options": []}
def fake_create(*_):
"""Fake venv create.
This is for the test to avoid network usage on venv creation, but also create
and set the fake dir.
"""
os.mkdir(fake_venv_path)
builder.env_path = fake_venv_path
fake_venv_path = tempfile.TemporaryDirectory().name
builder.create_with_external_venv = fake_create
builder.create_env('python', False, options=options)
assert os.path.exists(builder.env_path)
cache_mock = Mock()
envbuilder.destroy_venv(builder.env_path, cache_mock)
self.assertFalse(os.path.exists(builder.env_path))
cache_mock.remove.assert_called_with(builder.env_path)
def test_destroy_venv_if_env_path_not_found(self):
builder = envbuilder._FadesEnvBuilder()
assert not os.path.exists(builder.env_path)
cache_mock = Mock()
envbuilder.destroy_venv(builder.env_path, cache_mock)
self.assertFalse(os.path.exists(builder.env_path))
cache_mock.remove.assert_called_with(builder.env_path)
class UsageManagerTestCase(unittest.TestCase):
def setUp(self):
temp_file_descriptor, self.tempfile = tempfile.mkstemp(prefix="test-temp-file")
os.close(temp_file_descriptor)
self.temp_folder = tempfile.mkdtemp()
self.file_path = Path(self.temp_folder) / 'usage_stats'
self.addCleanup(lambda: os.path.exists(self.tempfile) and os.remove(self.tempfile))
self.addCleanup(shutil.rmtree, self.temp_folder, ignore_errors=True)
self.uuids = ['env1', 'env2', 'env3']
self.venvscache = cache.VEnvsCache(Path(self.tempfile))
for uuid in self.uuids:
metadata = {'env_path': os.path.join(self.temp_folder, uuid), "env_bin_path": ""}
self.venvscache.store('', metadata, '', '')
def get_usage_lines(self, manager):
self.assertTrue(os.path.exists(self.file_path), msg="File usage exists")
lines = []
for line in open(self.file_path).readlines():
uuid, d = line.split()
d = manager._str_to_datetime(d)
lines.append((uuid, d))
return lines
def test_file_usage_dont_exists_then_it_is_created_and_initialized(self):
self.assertFalse(os.path.exists(self.file_path), msg="First file doesn't exists")
manager = envbuilder.UsageManager(self.file_path, self.venvscache)
lines = self.get_usage_lines(manager)
self.assertEqual(len(lines), len(self.uuids), msg="File have one line per venv")
pending_uuids = self.uuids[:]
for uuid, dt in lines:
self.assertTrue(uuid in pending_uuids, msg="Every uuid is in file")
pending_uuids.remove(uuid)
def test_usage_record_is_recorded(self):
manager = envbuilder.UsageManager(self.file_path, self.venvscache)
lines = self.get_usage_lines(manager)
self.assertEqual(len(lines), len(self.uuids), msg="File have one line per venv")
venv = self.venvscache.get_venv(uuid=self.uuids[0])
manager.store_usage_stat(venv, self.venvscache)
lines = self.get_usage_lines(manager)
self.assertEqual(2, len([1 for u, d in lines if u == self.uuids[0]]),
msg="Selected uuid is two times in file")
def test_usage_file_is_compacted_when_though_no_venv_is_removed(self):
old_date = datetime.now()
new_date = old_date + timedelta(days=1)
with patch('fades.envbuilder.datetime') as mock_datetime:
mock_datetime.now.return_value = old_date
mock_datetime.strptime.side_effect = lambda *args, **kw: datetime.strptime(*args, **kw)
mock_datetime.strftime.side_effect = lambda *args, **kw: datetime.strftime(*args, **kw)
manager = envbuilder.UsageManager(self.file_path, self.venvscache)
lines = self.get_usage_lines(manager)
for u, d in lines:
self.assertEqual(old_date, d, msg="All records have the same date")
venv = self.venvscache.get_venv(uuid=self.uuids[0])
manager.store_usage_stat(venv, self.venvscache)
mock_datetime.now.return_value = new_date
manager.store_usage_stat(venv, self.venvscache)
lines = self.get_usage_lines(manager)
self.assertEqual(len(self.uuids) + 2, len(lines))
manager.clean_unused_venvs(4)
lines = self.get_usage_lines(manager)
self.assertEqual(len(self.uuids), len(lines))
for u, d in lines:
if u == self.uuids[0]:
self.assertEqual(new_date, d, msg="Selected env have new date")
else:
self.assertEqual(old_date, d, msg="Others envs have old date")
def test_executionerror_exception(self):
env_builder = envbuilder._FadesEnvBuilder()
interpreter = 'python3'
is_current = False
options = {"venv_options": []}
with patch('fades.envbuilder.helpers.logged_exec') as mock_lexec:
mock_lexec.side_effect = envbuilder.helpers.ExecutionError(1, 'cmd', ['stdout'])
with self.assertRaises(FadesError) as cm:
env_builder.create_env(interpreter, is_current, options)
self.assertEqual(str(cm.exception), "Failed to run venv module externally")
def test_general_error_exception(self):
env_builder = envbuilder._FadesEnvBuilder()
interpreter = 'python3'
is_current = False
options = {"venv_options": []}
with patch('fades.envbuilder.helpers.logged_exec') as mock_lexec:
mock_lexec.side_effect = Exception()
with self.assertRaises(FadesError) as cm:
env_builder.create_env(interpreter, is_current, options)
self.assertEqual(str(cm.exception), "General error while running external venv")
def test_when_a_venv_is_removed_it_is_removed_from_everywhere(self):
old_date = datetime.now()
new_date = old_date + timedelta(days=5)
with patch('fades.envbuilder.datetime') as mock_datetime:
mock_datetime.now.return_value = old_date
mock_datetime.strptime.side_effect = lambda *args, **kw: datetime.strptime(*args, **kw)
mock_datetime.strftime.side_effect = lambda *args, **kw: datetime.strftime(*args, **kw)
manager = envbuilder.UsageManager(self.file_path, self.venvscache)
lines = self.get_usage_lines(manager)
for u, d in lines:
self.assertEqual(old_date, d, msg="All records have the same date")
venv = self.venvscache.get_venv(uuid=self.uuids[0])
manager.store_usage_stat(venv, self.venvscache)
mock_datetime.now.return_value = new_date
manager.store_usage_stat(venv, self.venvscache)
lines = self.get_usage_lines(manager)
self.assertEqual(len(self.uuids) + 2, len(lines))
with patch('fades.envbuilder.destroy_venv') as destroy_venv_mock:
manager.clean_unused_venvs(4)
lines = self.get_usage_lines(manager)
self.assertEqual(1, len(lines), msg="Only one venv remains alive")
uuid, d = lines[0]
self.assertEqual(self.uuids[0], uuid,
msg="The env who survive is the last used one.")
# destroy_env and cache.remove was called for the others
for uuid in self.uuids[1:]:
env_path = self.venvscache.get_venv(uuid=uuid)['env_path']
destroy_venv_mock.assert_any_call(env_path, self.venvscache)
fades-master/tests/test_cache/ 0000775 0001750 0001750 00000000000 15215454131 015700 5 ustar tchet tchet fades-master/tests/test_cache/test_store.py 0000664 0001750 0001750 00000004016 15215454131 020446 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
import json
from fades import cache
def test_missing_file(tmp_file):
venvscache = cache.VEnvsCache(tmp_file)
metadata = {"env_path": "some/path", "env_bin_path": "other/path", "extra": "foobar"}
venvscache.store('installed', metadata, 'interpreter', 'options')
with open(tmp_file, 'rt', encoding='utf8') as fh:
data = json.loads(fh.readline())
assert 'timestamp' in data
assert data['installed'] == 'installed'
assert data['metadata'] == metadata
assert data['interpreter'] == 'interpreter'
assert data['options'] == 'options'
def test_with_previous_content(tmp_file):
with open(tmp_file, 'wt', encoding='utf8') as fh:
fh.write(json.dumps({'foo': 'bar'}) + '\n')
venvscache = cache.VEnvsCache(tmp_file)
metadata = {"env_path": "some/path", "env_bin_path": "other/path", "extra": "foobar"}
venvscache.store('installed', metadata, 'interpreter', 'options')
with open(tmp_file, 'rt', encoding='utf8') as fh:
data = json.loads(fh.readline())
assert data, {'foo': 'bar'}
data = json.loads(fh.readline())
assert 'timestamp' in data
assert data['installed'] == 'installed'
assert data['metadata'] == metadata
assert data['interpreter'] == 'interpreter'
assert data['options'] == 'options'
fades-master/tests/test_cache/test_selection.py 0000664 0001750 0001750 00000022224 15215454131 021300 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
import uuid
from fades import helpers, parsing
from tests import get_reqs
def test_empty(venvscache):
resp = venvscache._select([], {}, 'pythonX.Y', 'options')
assert resp is None
def test_nomatch_repo_dependency(venvscache, fake_venv):
reqs = {"repoloco": get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'pypi': {'dep': '5'}})
resp = venvscache._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_nomatch_pypi_dependency(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep1 == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'pypi': {'dep2': '5'}})
resp = venvscache._select([venv], reqs, interpreter, uuid='', options=options)
resp is None
def test_nomatch_vcs_dependency(venvscache, fake_venv):
reqs = {'vcs': [parsing.VCSDependency('someurl')]}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'vcs': {'otherurl': None}})
resp = venvscache._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_nomatch_version(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'pypi': {'dep': '7'}})
resp = venvscache._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_simple_pypi_match(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv("foobar", installed={'pypi': {'dep': '5'}})
resp = venvscache._select([venv], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == 'foobar'
def test_simple_vcs_match(venvscache, fake_venv):
reqs = {'vcs': [parsing.VCSDependency('someurl')]}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv("foobar", installed={'vcs': {'someurl': None}})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == 'foobar'
def test_match_mixed_single(venvscache, fake_venv):
reqs = {'vcs': [parsing.VCSDependency('someurl')], 'pypi': get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv1 = fake_venv("foobar1", installed={'vcs': {'someurl': None}, 'pypi': {'dep': '5'}})
venv2 = fake_venv("foobar2", installed={'pypi': {'dep': '5'}})
venv3 = fake_venv("foobar3", installed={'vcs': {'someurl': None}})
resp = venvscache ._select(
[venv1, venv2, venv3], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == 'foobar1'
def test_match_mixed_multiple(venvscache, fake_venv):
reqs = {'vcs': [parsing.VCSDependency('url1'), parsing.VCSDependency('url2')],
'pypi': get_reqs('dep1 == 5', 'dep2')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv("foobar", installed={
'vcs': {'url1': None, 'url2': None},
'pypi': {'dep1': '5', 'dep2': '7'},
})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == 'foobar'
def test_match_noversion(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv("foobar", installed={'pypi': {'dep': '5'}})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == 'foobar'
def test_middle_match(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv1 = fake_venv("venv1", installed={'pypi': {'dep': '3'}})
venv2 = fake_venv("venv2", installed={'pypi': {'dep': '5'}})
venv3 = fake_venv("venv3", installed={'pypi': {'dep': '7'}})
resp = venvscache ._select([venv1, venv2, venv3], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == 'venv2'
def test_multiple_match_bigger_version(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv1 = fake_venv("venv1", installed={'pypi': {'dep': '3'}})
venv2 = fake_venv("venv2", installed={'pypi': {'dep': '7'}})
venv3 = fake_venv("venv3", installed={'pypi': {'dep': '5'}})
resp = venvscache ._select([venv1, venv2, venv3], reqs, interpreter, uuid='',
options=options)
# matches venv2 because it has the bigger version for 'dep' (even if it's not the
# latest virtualenv created)
assert resp["extra"] == 'venv2'
def test_multiple_deps_ok(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep1 == 5', 'dep2 == 7')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv("foobar", installed={'pypi': {'dep1': '5', 'dep2': '7'}})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == 'foobar'
def test_multiple_deps_just_one(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep1 == 5', 'dep2 == 7')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'pypi': {'dep1': '5', 'dep2': '2'}})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_not_too_crowded(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep1')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'pypi': {'dep1': '5', 'dep2': '2'}})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_same_quantity_different_deps(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep1', 'dep2')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'pypi': {'dep1': '5', 'dep3': '2'}})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_no_requirements_some_installed(venvscache, fake_venv):
reqs = {}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv(installed={'pypi': {'dep1': '5', 'dep3': '2'}})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_no_requirements_empty_venv(venvscache, fake_venv):
reqs = {}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv("foobar", installed={})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == "foobar"
def test_simple_match_empty_options(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {}
venv = fake_venv("foobar", installed={'pypi': {'dep': '5'}}, options={})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == "foobar"
def test_no_match_due_to_options(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv = fake_venv("foobar", installed={'pypi': {'dep': '5'}}, options={})
resp = venvscache ._select([venv], reqs, interpreter, uuid='', options=options)
assert resp is None
def test_match_due_to_options(venvscache, fake_venv):
reqs = {'pypi': get_reqs('dep == 5')}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv1 = fake_venv("venv1", installed={'pypi': {'dep': '5'}}, options={})
venv2 = fake_venv("venv2", installed={'pypi': {'dep': '5'}}, options={'foo': 'bar'})
resp = venvscache ._select([venv1, venv2], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == "venv2"
def test_no_deps_but_options(venvscache, fake_venv):
reqs = {}
interpreter = 'pythonX.Y'
options = {'foo': 'bar'}
venv1 = fake_venv("venv1", installed={}, options={})
venv2 = fake_venv("venv2", installed={}, options={'foo': 'bar'})
resp = venvscache ._select([venv1, venv2], reqs, interpreter, uuid='', options=options)
assert resp["extra"] == "venv2"
def test_match_uuid(venvscache, fake_venv):
venv_uuid = str(uuid.uuid4())
metadata = {
"env_path": str(helpers.get_basedir() / venv_uuid),
"env_bin_path": str(helpers.get_basedir() / "other"),
"extra": "foobar",
}
venv = fake_venv(metadata=metadata, installed={})
resp = venvscache ._select([venv], uuid=venv_uuid)
assert resp["extra"] == "foobar"
fades-master/tests/test_cache/test_remove.py 0000664 0001750 0001750 00000007752 15215454131 020621 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
import json
import os
import time
from pathlib import Path
from threading import Thread
from fades import cache
def test_missing_file(tmp_file):
venvscache = cache.VEnvsCache(tmp_file)
venvscache.remove("missing/path")
lines = venvscache._read_cache()
assert lines == []
def test_missing_env_in_cache(tmp_file):
venvscache = cache.VEnvsCache(tmp_file)
options = {"foo": "bar"}
metadata = {"env_path": "some/path", "env_bin_path": "other/path"}
venvscache.store("installed", metadata, "interpreter", options=options)
lines = venvscache._read_cache()
assert len(lines) == 1
venvscache.remove(Path("some/path"))
lines = venvscache._read_cache()
assert lines == []
def test_preserve_cache_data_ordering(tmp_file):
venvscache = cache.VEnvsCache(tmp_file)
# store 3 venvs
options = {"foo": "bar"}
metadata1 = {"env_path": "path/env1", "env_bin_path": "other/path"}
metadata2 = {"env_path": "path/env2", "env_bin_path": "other/path"}
metadata3 = {"env_path": "path/env3", "env_bin_path": "other/path"}
venvscache.store("installed1", metadata1, "interpreter", options=options)
venvscache.store("installed2", metadata2, "interpreter", options=options)
venvscache.store("installed3", metadata3, "interpreter", options=options)
venvscache.remove(Path("path/env2"))
lines = venvscache._read_cache()
assert len(lines) == 2
assert json.loads(lines[0]).get("metadata").get("env_path") == "path/env1"
assert json.loads(lines[1]).get("metadata").get("env_path") == "path/env3"
def test_lock_cache_for_remove(tmp_file):
venvscache = cache.VEnvsCache(tmp_file)
# store 3 venvs
options = {"foo": "bar"}
metadata1 = {"env_path": "path/env1", "env_bin_path": "other/path"}
metadata2 = {"env_path": "path/env2", "env_bin_path": "other/path"}
metadata3 = {"env_path": "path/env3", "env_bin_path": "other/path"}
venvscache.store("installed1", metadata1, "interpreter", options=options)
venvscache.store("installed2", metadata2, "interpreter", options=options)
venvscache.store("installed3", metadata3, "interpreter", options=options)
# patch _write_cache so it emulates a slow write during which
# another process managed to modify the cache file before the
# first process finished writing the modified cache data
original_write_cache = venvscache._write_cache
other_process = Thread(target=venvscache.remove, args=(Path("path/env1"),))
def slow_write_cache(*args, **kwargs):
venvscache._write_cache = original_write_cache
# start "other process" and wait a little to ensure it must wait
# for the lock to be released
other_process.start()
time.sleep(0.01)
original_write_cache(*args, **kwargs)
venvscache._write_cache = slow_write_cache
# just a sanity check
assert not os.path.exists(venvscache.filepath.name + ".lock")
# remove a virtualenv from the cache
venvscache.remove(Path("path/env2"))
other_process.join()
# when cache file is properly locked both virtualenvs
# will have been removed from the cache
lines = venvscache._read_cache()
assert len(lines) == 1
assert json.loads(lines[0])["metadata"]["env_path"] == "path/env3"
assert not os.path.exists(venvscache.filepath.name + ".lock")
fades-master/tests/test_cache/test_comparisons.py 0000664 0001750 0001750 00000007673 15215454131 021663 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
import pytest
from fades import parsing
from tests import get_reqs
from tests.test_cache import get_distrib
@pytest.mark.parametrize("req,installed,expected", [
# Equal
("==5", "5", "ok"),
("==5", "2", None),
# Greater than
(">5", "4", None),
(">5", "5", None),
(">5", "6", "ok"),
# Greater than or equal
(">=5", "4", None),
(">=5", "5", "ok"),
(">=5", "6", "ok"),
# Less than
("<5", "4", "ok"),
("<5", "5", None),
("<5", "6", None),
# Less than or equal
("<=5", "4", "ok"),
("<=5", "5", "ok"),
("<=5", "6", None),
# Complex cases
("== 2.5", "2.5.0", "ok"),
("> 2.7", "2.12", "ok"),
("> 2.7a0", "2.7", "ok"),
("> 2.7", "2.7a0", None),
# Crazy picky
(">1.6,<1.9,!=1.9.6", "1.5.0", None),
(">1.6,<1.9,!=1.9.6", "1.6.7", "ok"),
(">1.6,<1.9,!=1.8.6", "1.8.7", "ok"),
(">1.6,<1.9,!=1.9.6", "1.9.6", None),
])
def test_check_versions(venvscache, fake_venv, req, installed, expected):
"""The comparison in the selection."""
reqs = {"pypi": get_reqs("dep" + req)}
interpreter = "pythonX.Y"
options = {"foo": "bar"}
venv = fake_venv("ok", installed={"pypi": {"dep": installed}})
resp = venvscache._select([venv], reqs, interpreter, uuid="", options=options)
if expected is None:
assert resp is None
else:
assert resp["extra"] == expected
@pytest.mark.parametrize("possible_venvs", [
[
(get_distrib(('dep', '3')), 'venv_best_fit'),
],
[
(get_distrib(('dep1', '3'), ('dep2', '3')), 'venv_best_fit'),
],
[
(get_distrib(('dep', '5')), 'venv_best_fit'),
(get_distrib(('dep', '3')), 'venv_1'),
],
[
(get_distrib(('dep1', '5'), ('dep2', '7')), 'venv_best_fit'),
(get_distrib(('dep1', '3'), ('dep2', '6')), 'venv_1'),
],
[
(get_distrib(('dep1', '3'), ('dep2', '9')), 'venv_1'),
(get_distrib(('dep1', '5'), ('dep2', '7')), 'venv_best_fit'),
],
[
(get_distrib(('dep1', '5'), ('dep2', '7')), 'venv_1'),
(get_distrib(('dep1', '3'), ('dep2', '9')), 'venv_best_fit'),
],
[
(get_distrib(('dep1', '3'), ('dep2', '9'), ('dep3', '4')), 'venv_best_fit'),
(get_distrib(('dep1', '5'), ('dep2', '7'), ('dep3', '2')), 'venv_1'),
],
[
(get_distrib(('dep2', '3'), ('dep1', '2'), ('dep3', '8')), 'venv_best_fit'),
(get_distrib(('dep1', '7'), ('dep3', '5'), ('dep2', '2')), 'venv_1'),
],
[
(get_distrib(('dep1', '3'), ('dep2', '2')), 'venv_1'),
(get_distrib(('dep1', '4'), ('dep2', '2')), 'venv_2'),
(get_distrib(('dep1', '5'), ('dep2', '7')), 'venv_best_fit'),
(get_distrib(('dep1', '5'), ('dep2', '6')), 'venv_3'),
],
[
([parsing.VCSDependency('someurl')], 'venv_best_fit'),
],
[
([parsing.VCSDependency('someurl')] + get_distrib(('dep', '3')), 'venv_best_fit'),
],
[
([parsing.VCSDependency('someurl')] + get_distrib(('dep', '3')), 'venv_best_fit'),
([parsing.VCSDependency('someurl')] + get_distrib(('dep', '1')), 'venv_1'),
],
])
def test_best_fit(venvscache, possible_venvs):
"""Check the venv best fitting decissor."""
assert venvscache._select_better_fit(possible_venvs) == 'venv_best_fit'
fades-master/tests/test_cache/test_caches.py 0000664 0001750 0001750 00000004561 15215454131 020545 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
from unittest.mock import patch
from fades import cache
def test_missing_file_pytest(tmp_file):
venvscache = cache.VEnvsCache(tmp_file)
with patch.object(venvscache, '_select', return_value=None) as mock:
resp = venvscache.get_venv('requirements', 'interpreter', uuid='', options='options')
mock.assert_called_with([], 'requirements', 'interpreter', uuid='', options='options')
assert not resp
def test_empty_file_pytest(tmp_file):
open(tmp_file, 'wt', encoding='utf8').close()
venvscache = cache.VEnvsCache(tmp_file)
with patch.object(venvscache, '_select', return_value=None) as mock:
resp = venvscache.get_venv('requirements', 'interpreter')
mock.assert_called_with([], 'requirements', 'interpreter', uuid='', options=None)
assert not resp
def test_some_file_content_pytest(tmp_file):
with open(tmp_file, 'wt', encoding='utf8') as fh:
fh.write('foo\nbar\n')
venvscache = cache.VEnvsCache(tmp_file)
with patch.object(venvscache, '_select', return_value="resp") as mock:
resp = venvscache.get_venv('requirements', 'interpreter', uuid='', options='options')
mock.assert_called_with(['foo', 'bar'], 'requirements', 'interpreter', uuid='',
options='options')
assert resp == 'resp'
def test_get_by_uuid_pytest(tmp_file):
with open(tmp_file, 'wt', encoding='utf8') as fh:
fh.write('foo\nbar\n')
venvscache = cache.VEnvsCache(tmp_file)
with patch.object(venvscache, '_select', return_value='resp') as mock:
resp = venvscache.get_venv(uuid='uuid')
mock.assert_called_with(['foo', 'bar'], None, '', uuid='uuid', options=None)
assert resp == 'resp'
fades-master/tests/test_cache/conftest.py 0000664 0001750 0001750 00000001702 15215454131 020077 0 ustar tchet tchet # Copyright 2015-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
from pytest import fixture
from fades import cache
@fixture(scope="function")
def venvscache(tmp_path):
"""Fixture for a cache file for virtualenvs."""
venvs_cache = cache.VEnvsCache(tmp_path / "test_venv_cache")
return venvs_cache
fades-master/tests/test_cache/__init__.py 0000664 0001750 0001750 00000001710 15215454131 020010 0 ustar tchet tchet # Copyright 2015-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Helpers for the Cache tests collection."""
from fades.parsing import NameVerDependency
def get_distrib(*dep_ver_pairs):
"""Build some Distributions with indicated info."""
return [NameVerDependency(dep, ver) for dep, ver in dep_ver_pairs]
fades-master/tests/integtest.py 0000664 0001750 0001750 00000002147 15215454131 016162 0 ustar tchet tchet # Copyright 2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Helper to run integration tests.
This is not part of the regular test suite, but a test that is used in a very specific
way from the integration tests defined in the Github CI infrastructure.
"""
import sys
def test_assert_python_version(pytestconfig):
expected = pytestconfig.getoption("integtest_pyversion")
vi = sys.version_info
current = f"{vi.major}.{vi.minor}"
assert current == expected
fades-master/tests/examples/ 0000775 0001750 0001750 00000000000 15215454131 015414 5 ustar tchet tchet fades-master/tests/examples/pypi_get_version_ok.json 0000664 0001750 0001750 00000307716 15215454131 022403 0 ustar tchet tchet {
"info": {
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"cheesecake_code_kwalitee_id": null,
"keywords": null,
"package_url": "http://pypi.python.org/pypi/requests",
"author": "Kenneth Reitz",
"author_email": "me@kennethreitz.com",
"download_url": "UNKNOWN",
"platform": "UNKNOWN",
"version": "2.8.1",
"cheesecake_documentation_id": null,
"_pypi_hidden": false,
"description": "Requests: HTTP for Humans\n=========================\n\n.. image:: https://img.shields.io/pypi/v/requests.svg\n :target: https://pypi.python.org/pypi/requests\n\n.. image:: https://img.shields.io/pypi/dm/requests.svg\n :target: https://pypi.python.org/pypi/requests\n\n\n\n\nRequests is an Apache2 Licensed HTTP library, written in Python, for human\nbeings.\n\nMost existing Python modules for sending HTTP requests are extremely\nverbose and cumbersome. Python's builtin urllib2 module provides most of\nthe HTTP capabilities you should need, but the api is thoroughly broken.\nIt requires an enormous amount of work (even method overrides) to\nperform the simplest of tasks.\n\nThings shouldn't be this way. Not in Python.\n\n.. code-block:: python\n\n >>> r = requests.get('https://api.github.com', auth=('user', 'pass'))\n >>> r.status_code\n 204\n >>> r.headers['content-type']\n 'application/json'\n >>> r.text\n ...\n\nSee `the same code, without Requests `_.\n\nRequests allow you to send HTTP/1.1 requests. You can add headers, form data,\nmultipart files, and parameters with simple Python dictionaries, and access the\nresponse data in the same way. It's powered by httplib and `urllib3\n`_, but it does all the hard work and crazy\nhacks for you.\n\n\nFeatures\n--------\n\n- International Domains and URLs\n- Keep-Alive & Connection Pooling\n- Sessions with Cookie Persistence\n- Browser-style SSL Verification\n- Basic/Digest Authentication\n- Elegant Key/Value Cookies\n- Automatic Decompression\n- Unicode Response Bodies\n- Multipart File Uploads\n- Connection Timeouts\n- Thread-safety\n- HTTP(S) proxy support\n\n\nInstallation\n------------\n\nTo install Requests, simply:\n\n.. code-block:: bash\n\n $ pip install requests\n\n\nDocumentation\n-------------\n\nDocumentation is available at http://docs.python-requests.org/.\n\n\nContribute\n----------\n\n#. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug. There is a `Contributor Friendly`_ tag for issues that should be ideal for people who are not very familiar with the codebase yet.\n#. Fork `the repository`_ on GitHub to start making your changes to the **master** branch (or branch off of it).\n#. Write a test which shows that the bug was fixed or that the feature works as expected.\n#. Send a pull request and bug the maintainer until it gets merged and published. :) Make sure to add yourself to AUTHORS_.\n\n.. _`the repository`: http://github.com/kennethreitz/requests\n.. _AUTHORS: https://github.com/kennethreitz/requests/blob/master/AUTHORS.rst\n.. _Contributor Friendly: https://github.com/kennethreitz/requests/issues?direction=desc&labels=Contributor+Friendly&page=1&sort=updated&state=open\n\n\n.. :changelog:\n\nRelease History\n---------------\n\n2.8.1 (2015-10-13)\n++++++++++++++++++\n\n**Bugfixes**\n\n- Update certificate bundle to match ``certifi`` 2015.9.6.2's weak certificate\n bundle.\n- Fix a bug in 2.8.0 where requests would raise ``ConnectTimeout`` instead of\n ``ConnectionError``\n- When using the PreparedRequest flow, requests will now correctly respect the\n ``json`` parameter. Broken in 2.8.0.\n- When using the PreparedRequest flow, requests will now correctly handle a\n Unicode-string method name on Python 2. Broken in 2.8.0.\n\n2.8.0 (2015-10-05)\n++++++++++++++++++\n\n**Minor Improvements** (Backwards Compatible)\n\n- Requests now supports per-host proxies. This allows the ``proxies``\n dictionary to have entries of the form\n ``{'://': ''}``. Host-specific proxies will be used\n in preference to the previously-supported scheme-specific ones, but the\n previous syntax will continue to work.\n- ``Response.raise_for_status`` now prints the URL that failed as part of the\n exception message.\n- ``requests.utils.get_netrc_auth`` now takes an ``raise_errors`` kwarg,\n defaulting to ``False``. When ``True``, errors parsing ``.netrc`` files cause\n exceptions to be thrown.\n- Change to bundled projects import logic to make it easier to unbundle\n requests downstream.\n- Changed the default User-Agent string to avoid leaking data on Linux: now\n contains only the requests version.\n\n**Bugfixes**\n\n- The ``json`` parameter to ``post()`` and friends will now only be used if\n neither ``data`` nor ``files`` are present, consistent with the\n documentation.\n- We now ignore empty fields in the ``NO_PROXY`` enviroment variable.\n- Fixed problem where ``httplib.BadStatusLine`` would get raised if combining\n ``stream=True`` with ``contextlib.closing``.\n- Prevented bugs where we would attempt to return the same connection back to\n the connection pool twice when sending a Chunked body.\n- Miscellaneous minor internal changes.\n- Digest Auth support is now thread safe.\n\n**Updates**\n\n- Updated urllib3 to 1.12.\n\n2.7.0 (2015-05-03)\n++++++++++++++++++\n\nThis is the first release that follows our new release process. For more, see\n`our documentation\n`_.\n\n**Bugfixes**\n\n- Updated urllib3 to 1.10.4, resolving several bugs involving chunked transfer\n encoding and response framing.\n\n2.6.2 (2015-04-23)\n++++++++++++++++++\n\n**Bugfixes**\n\n- Fix regression where compressed data that was sent as chunked data was not\n properly decompressed. (#2561)\n\n2.6.1 (2015-04-22)\n++++++++++++++++++\n\n**Bugfixes**\n\n- Remove VendorAlias import machinery introduced in v2.5.2.\n\n- Simplify the PreparedRequest.prepare API: We no longer require the user to\n pass an empty list to the hooks keyword argument. (c.f. #2552)\n\n- Resolve redirects now receives and forwards all of the original arguments to\n the adapter. (#2503)\n\n- Handle UnicodeDecodeErrors when trying to deal with a unicode URL that\n cannot be encoded in ASCII. (#2540)\n\n- Populate the parsed path of the URI field when performing Digest\n Authentication. (#2426)\n\n- Copy a PreparedRequest's CookieJar more reliably when it is not an instance\n of RequestsCookieJar. (#2527)\n\n2.6.0 (2015-03-14)\n++++++++++++++++++\n\n**Bugfixes**\n\n- CVE-2015-2296: Fix handling of cookies on redirect. Previously a cookie\n without a host value set would use the hostname for the redirected URL\n exposing requests users to session fixation attacks and potentially cookie\n stealing. This was disclosed privately by Matthew Daley of\n `BugFuzz `_. This affects all versions of requests from\n v2.1.0 to v2.5.3 (inclusive on both ends).\n\n- Fix error when requests is an ``install_requires`` dependency and ``python\n setup.py test`` is run. (#2462)\n\n- Fix error when urllib3 is unbundled and requests continues to use the\n vendored import location.\n\n- Include fixes to ``urllib3``'s header handling.\n\n- Requests' handling of unvendored dependencies is now more restrictive.\n\n**Features and Improvements**\n\n- Support bytearrays when passed as parameters in the ``files`` argument.\n (#2468)\n\n- Avoid data duplication when creating a request with ``str``, ``bytes``, or\n ``bytearray`` input to the ``files`` argument.\n\n2.5.3 (2015-02-24)\n++++++++++++++++++\n\n**Bugfixes**\n\n- Revert changes to our vendored certificate bundle. For more context see\n (#2455, #2456, and http://bugs.python.org/issue23476)\n\n2.5.2 (2015-02-23)\n++++++++++++++++++\n\n**Features and Improvements**\n\n- Add sha256 fingerprint support. (`shazow/urllib3#540`_)\n\n- Improve the performance of headers. (`shazow/urllib3#544`_)\n\n**Bugfixes**\n\n- Copy pip's import machinery. When downstream redistributors remove\n requests.packages.urllib3 the import machinery will continue to let those\n same symbols work. Example usage in requests' documentation and 3rd-party\n libraries relying on the vendored copies of urllib3 will work without having\n to fallback to the system urllib3.\n\n- Attempt to quote parts of the URL on redirect if unquoting and then quoting\n fails. (#2356)\n\n- Fix filename type check for multipart form-data uploads. (#2411)\n\n- Properly handle the case where a server issuing digest authentication\n challenges provides both auth and auth-int qop-values. (#2408)\n\n- Fix a socket leak. (`shazow/urllib3#549`_)\n\n- Fix multiple ``Set-Cookie`` headers properly. (`shazow/urllib3#534`_)\n\n- Disable the built-in hostname verification. (`shazow/urllib3#526`_)\n\n- Fix the behaviour of decoding an exhausted stream. (`shazow/urllib3#535`_)\n\n**Security**\n\n- Pulled in an updated ``cacert.pem``.\n\n- Drop RC4 from the default cipher list. (`shazow/urllib3#551`_)\n\n.. _shazow/urllib3#551: https://github.com/shazow/urllib3/pull/551\n.. _shazow/urllib3#549: https://github.com/shazow/urllib3/pull/549\n.. _shazow/urllib3#544: https://github.com/shazow/urllib3/pull/544\n.. _shazow/urllib3#540: https://github.com/shazow/urllib3/pull/540\n.. _shazow/urllib3#535: https://github.com/shazow/urllib3/pull/535\n.. _shazow/urllib3#534: https://github.com/shazow/urllib3/pull/534\n.. _shazow/urllib3#526: https://github.com/shazow/urllib3/pull/526\n\n2.5.1 (2014-12-23)\n++++++++++++++++++\n\n**Behavioural Changes**\n\n- Only catch HTTPErrors in raise_for_status (#2382)\n\n**Bugfixes**\n\n- Handle LocationParseError from urllib3 (#2344)\n- Handle file-like object filenames that are not strings (#2379)\n- Unbreak HTTPDigestAuth handler. Allow new nonces to be negotiated (#2389)\n\n2.5.0 (2014-12-01)\n++++++++++++++++++\n\n**Improvements**\n\n- Allow usage of urllib3's Retry object with HTTPAdapters (#2216)\n- The ``iter_lines`` method on a response now accepts a delimiter with which\n to split the content (#2295)\n\n**Behavioural Changes**\n\n- Add deprecation warnings to functions in requests.utils that will be removed\n in 3.0 (#2309)\n- Sessions used by the functional API are always closed (#2326)\n- Restrict requests to HTTP/1.1 and HTTP/1.0 (stop accepting HTTP/0.9) (#2323)\n\n**Bugfixes**\n\n- Only parse the URL once (#2353)\n- Allow Content-Length header to always be overridden (#2332)\n- Properly handle files in HTTPDigestAuth (#2333)\n- Cap redirect_cache size to prevent memory abuse (#2299)\n- Fix HTTPDigestAuth handling of redirects after authenticating successfully\n (#2253)\n- Fix crash with custom method parameter to Session.request (#2317)\n- Fix how Link headers are parsed using the regular expression library (#2271)\n\n**Documentation**\n\n- Add more references for interlinking (#2348)\n- Update CSS for theme (#2290)\n- Update width of buttons and sidebar (#2289)\n- Replace references of Gittip with Gratipay (#2282)\n- Add link to changelog in sidebar (#2273)\n\n2.4.3 (2014-10-06)\n++++++++++++++++++\n\n**Bugfixes**\n\n- Unicode URL improvements for Python 2.\n- Re-order JSON param for backwards compat.\n- Automatically defrag authentication schemes from host/pass URIs. (`#2249 `_)\n\n\n2.4.2 (2014-10-05)\n++++++++++++++++++\n\n**Improvements**\n\n- FINALLY! Add json parameter for uploads! (`#2258 `_)\n- Support for bytestring URLs on Python 3.x (`#2238 `_)\n\n**Bugfixes**\n\n- Avoid getting stuck in a loop (`#2244 `_)\n- Multiple calls to iter* fail with unhelpful error. (`#2240 `_, `#2241 `_)\n\n**Documentation**\n\n- Correct redirection introduction (`#2245 `_)\n- Added example of how to send multiple files in one request. (`#2227 `_)\n- Clarify how to pass a custom set of CAs (`#2248 `_)\n\n\n\n2.4.1 (2014-09-09)\n++++++++++++++++++\n\n- Now has a \"security\" package extras set, ``$ pip install requests[security]``\n- Requests will now use Certifi if it is available.\n- Capture and re-raise urllib3 ProtocolError\n- Bugfix for responses that attempt to redirect to themselves forever (wtf?).\n\n\n2.4.0 (2014-08-29)\n++++++++++++++++++\n\n**Behavioral Changes**\n\n- ``Connection: keep-alive`` header is now sent automatically.\n\n**Improvements**\n\n- Support for connect timeouts! Timeout now accepts a tuple (connect, read) which is used to set individual connect and read timeouts.\n- Allow copying of PreparedRequests without headers/cookies.\n- Updated bundled urllib3 version.\n- Refactored settings loading from environment -- new `Session.merge_environment_settings`.\n- Handle socket errors in iter_content.\n\n\n2.3.0 (2014-05-16)\n++++++++++++++++++\n\n**API Changes**\n\n- New ``Response`` property ``is_redirect``, which is true when the\n library could have processed this response as a redirection (whether\n or not it actually did).\n- The ``timeout`` parameter now affects requests with both ``stream=True`` and\n ``stream=False`` equally.\n- The change in v2.0.0 to mandate explicit proxy schemes has been reverted.\n Proxy schemes now default to ``http://``.\n- The ``CaseInsensitiveDict`` used for HTTP headers now behaves like a normal\n dictionary when references as string or viewed in the interpreter.\n\n**Bugfixes**\n\n- No longer expose Authorization or Proxy-Authorization headers on redirect.\n Fix CVE-2014-1829 and CVE-2014-1830 respectively.\n- Authorization is re-evaluated each redirect.\n- On redirect, pass url as native strings.\n- Fall-back to autodetected encoding for JSON when Unicode detection fails.\n- Headers set to ``None`` on the ``Session`` are now correctly not sent.\n- Correctly honor ``decode_unicode`` even if it wasn't used earlier in the same\n response.\n- Stop advertising ``compress`` as a supported Content-Encoding.\n- The ``Response.history`` parameter is now always a list.\n- Many, many ``urllib3`` bugfixes.\n\n2.2.1 (2014-01-23)\n++++++++++++++++++\n\n**Bugfixes**\n\n- Fixes incorrect parsing of proxy credentials that contain a literal or encoded '#' character.\n- Assorted urllib3 fixes.\n\n2.2.0 (2014-01-09)\n++++++++++++++++++\n\n**API Changes**\n\n- New exception: ``ContentDecodingError``. Raised instead of ``urllib3``\n ``DecodeError`` exceptions.\n\n**Bugfixes**\n\n- Avoid many many exceptions from the buggy implementation of ``proxy_bypass`` on OS X in Python 2.6.\n- Avoid crashing when attempting to get authentication credentials from ~/.netrc when running as a user without a home directory.\n- Use the correct pool size for pools of connections to proxies.\n- Fix iteration of ``CookieJar`` objects.\n- Ensure that cookies are persisted over redirect.\n- Switch back to using chardet, since it has merged with charade.\n\n2.1.0 (2013-12-05)\n++++++++++++++++++\n\n- Updated CA Bundle, of course.\n- Cookies set on individual Requests through a ``Session`` (e.g. via ``Session.get()``) are no longer persisted to the ``Session``.\n- Clean up connections when we hit problems during chunked upload, rather than leaking them.\n- Return connections to the pool when a chunked upload is successful, rather than leaking it.\n- Match the HTTPbis recommendation for HTTP 301 redirects.\n- Prevent hanging when using streaming uploads and Digest Auth when a 401 is received.\n- Values of headers set by Requests are now always the native string type.\n- Fix previously broken SNI support.\n- Fix accessing HTTP proxies using proxy authentication.\n- Unencode HTTP Basic usernames and passwords extracted from URLs.\n- Support for IP address ranges for no_proxy environment variable\n- Parse headers correctly when users override the default ``Host:`` header.\n- Avoid munging the URL in case of case-sensitive servers.\n- Looser URL handling for non-HTTP/HTTPS urls.\n- Accept unicode methods in Python 2.6 and 2.7.\n- More resilient cookie handling.\n- Make ``Response`` objects pickleable.\n- Actually added MD5-sess to Digest Auth instead of pretending to like last time.\n- Updated internal urllib3.\n- Fixed @Lukasa's lack of taste.\n\n2.0.1 (2013-10-24)\n++++++++++++++++++\n\n- Updated included CA Bundle with new mistrusts and automated process for the future\n- Added MD5-sess to Digest Auth\n- Accept per-file headers in multipart file POST messages.\n- Fixed: Don't send the full URL on CONNECT messages.\n- Fixed: Correctly lowercase a redirect scheme.\n- Fixed: Cookies not persisted when set via functional API.\n- Fixed: Translate urllib3 ProxyError into a requests ProxyError derived from ConnectionError.\n- Updated internal urllib3 and chardet.\n\n2.0.0 (2013-09-24)\n++++++++++++++++++\n\n**API Changes:**\n\n- Keys in the Headers dictionary are now native strings on all Python versions,\n i.e. bytestrings on Python 2, unicode on Python 3.\n- Proxy URLs now *must* have an explicit scheme. A ``MissingSchema`` exception\n will be raised if they don't.\n- Timeouts now apply to read time if ``Stream=False``.\n- ``RequestException`` is now a subclass of ``IOError``, not ``RuntimeError``.\n- Added new method to ``PreparedRequest`` objects: ``PreparedRequest.copy()``.\n- Added new method to ``Session`` objects: ``Session.update_request()``. This\n method updates a ``Request`` object with the data (e.g. cookies) stored on\n the ``Session``.\n- Added new method to ``Session`` objects: ``Session.prepare_request()``. This\n method updates and prepares a ``Request`` object, and returns the\n corresponding ``PreparedRequest`` object.\n- Added new method to ``HTTPAdapter`` objects: ``HTTPAdapter.proxy_headers()``.\n This should not be called directly, but improves the subclass interface.\n- ``httplib.IncompleteRead`` exceptions caused by incorrect chunked encoding\n will now raise a Requests ``ChunkedEncodingError`` instead.\n- Invalid percent-escape sequences now cause a Requests ``InvalidURL``\n exception to be raised.\n- HTTP 208 no longer uses reason phrase ``\"im_used\"``. Correctly uses\n ``\"already_reported\"``.\n- HTTP 226 reason added (``\"im_used\"``).\n\n**Bugfixes:**\n\n- Vastly improved proxy support, including the CONNECT verb. Special thanks to\n the many contributors who worked towards this improvement.\n- Cookies are now properly managed when 401 authentication responses are\n received.\n- Chunked encoding fixes.\n- Support for mixed case schemes.\n- Better handling of streaming downloads.\n- Retrieve environment proxies from more locations.\n- Minor cookies fixes.\n- Improved redirect behaviour.\n- Improved streaming behaviour, particularly for compressed data.\n- Miscellaneous small Python 3 text encoding bugs.\n- ``.netrc`` no longer overrides explicit auth.\n- Cookies set by hooks are now correctly persisted on Sessions.\n- Fix problem with cookies that specify port numbers in their host field.\n- ``BytesIO`` can be used to perform streaming uploads.\n- More generous parsing of the ``no_proxy`` environment variable.\n- Non-string objects can be passed in data values alongside files.\n\n1.2.3 (2013-05-25)\n++++++++++++++++++\n\n- Simple packaging fix\n\n\n1.2.2 (2013-05-23)\n++++++++++++++++++\n\n- Simple packaging fix\n\n\n1.2.1 (2013-05-20)\n++++++++++++++++++\n\n- 301 and 302 redirects now change the verb to GET for all verbs, not just\n POST, improving browser compatibility.\n- Python 3.3.2 compatibility\n- Always percent-encode location headers\n- Fix connection adapter matching to be most-specific first\n- new argument to the default connection adapter for passing a block argument\n- prevent a KeyError when there's no link headers\n\n1.2.0 (2013-03-31)\n++++++++++++++++++\n\n- Fixed cookies on sessions and on requests\n- Significantly change how hooks are dispatched - hooks now receive all the\n arguments specified by the user when making a request so hooks can make a\n secondary request with the same parameters. This is especially necessary for\n authentication handler authors\n- certifi support was removed\n- Fixed bug where using OAuth 1 with body ``signature_type`` sent no data\n- Major proxy work thanks to @Lukasa including parsing of proxy authentication\n from the proxy url\n- Fix DigestAuth handling too many 401s\n- Update vendored urllib3 to include SSL bug fixes\n- Allow keyword arguments to be passed to ``json.loads()`` via the\n ``Response.json()`` method\n- Don't send ``Content-Length`` header by default on ``GET`` or ``HEAD``\n requests\n- Add ``elapsed`` attribute to ``Response`` objects to time how long a request\n took.\n- Fix ``RequestsCookieJar``\n- Sessions and Adapters are now picklable, i.e., can be used with the\n multiprocessing library\n- Update charade to version 1.0.3\n\nThe change in how hooks are dispatched will likely cause a great deal of\nissues.\n\n1.1.0 (2013-01-10)\n++++++++++++++++++\n\n- CHUNKED REQUESTS\n- Support for iterable response bodies\n- Assume servers persist redirect params\n- Allow explicit content types to be specified for file data\n- Make merge_kwargs case-insensitive when looking up keys\n\n1.0.3 (2012-12-18)\n++++++++++++++++++\n\n- Fix file upload encoding bug\n- Fix cookie behavior\n\n1.0.2 (2012-12-17)\n++++++++++++++++++\n\n- Proxy fix for HTTPAdapter.\n\n1.0.1 (2012-12-17)\n++++++++++++++++++\n\n- Cert verification exception bug.\n- Proxy fix for HTTPAdapter.\n\n1.0.0 (2012-12-17)\n++++++++++++++++++\n\n- Massive Refactor and Simplification\n- Switch to Apache 2.0 license\n- Swappable Connection Adapters\n- Mountable Connection Adapters\n- Mutable ProcessedRequest chain\n- /s/prefetch/stream\n- Removal of all configuration\n- Standard library logging\n- Make Response.json() callable, not property.\n- Usage of new charade project, which provides python 2 and 3 simultaneous chardet.\n- Removal of all hooks except 'response'\n- Removal of all authentication helpers (OAuth, Kerberos)\n\nThis is not a backwards compatible change.\n\n0.14.2 (2012-10-27)\n+++++++++++++++++++\n\n- Improved mime-compatible JSON handling\n- Proxy fixes\n- Path hack fixes\n- Case-Insensistive Content-Encoding headers\n- Support for CJK parameters in form posts\n\n\n0.14.1 (2012-10-01)\n+++++++++++++++++++\n\n- Python 3.3 Compatibility\n- Simply default accept-encoding\n- Bugfixes\n\n\n0.14.0 (2012-09-02)\n++++++++++++++++++++\n\n- No more iter_content errors if already downloaded.\n\n0.13.9 (2012-08-25)\n+++++++++++++++++++\n\n- Fix for OAuth + POSTs\n- Remove exception eating from dispatch_hook\n- General bugfixes\n\n0.13.8 (2012-08-21)\n+++++++++++++++++++\n\n- Incredible Link header support :)\n\n0.13.7 (2012-08-19)\n+++++++++++++++++++\n\n- Support for (key, value) lists everywhere.\n- Digest Authentication improvements.\n- Ensure proxy exclusions work properly.\n- Clearer UnicodeError exceptions.\n- Automatic casting of URLs to strings (fURL and such)\n- Bugfixes.\n\n0.13.6 (2012-08-06)\n+++++++++++++++++++\n\n- Long awaited fix for hanging connections!\n\n0.13.5 (2012-07-27)\n+++++++++++++++++++\n\n- Packaging fix\n\n0.13.4 (2012-07-27)\n+++++++++++++++++++\n\n- GSSAPI/Kerberos authentication!\n- App Engine 2.7 Fixes!\n- Fix leaking connections (from urllib3 update)\n- OAuthlib path hack fix\n- OAuthlib URL parameters fix.\n\n0.13.3 (2012-07-12)\n+++++++++++++++++++\n\n- Use simplejson if available.\n- Do not hide SSLErrors behind Timeouts.\n- Fixed param handling with urls containing fragments.\n- Significantly improved information in User Agent.\n- client certificates are ignored when verify=False\n\n0.13.2 (2012-06-28)\n+++++++++++++++++++\n\n- Zero dependencies (once again)!\n- New: Response.reason\n- Sign querystring parameters in OAuth 1.0\n- Client certificates no longer ignored when verify=False\n- Add openSUSE certificate support\n\n0.13.1 (2012-06-07)\n+++++++++++++++++++\n\n- Allow passing a file or file-like object as data.\n- Allow hooks to return responses that indicate errors.\n- Fix Response.text and Response.json for body-less responses.\n\n0.13.0 (2012-05-29)\n+++++++++++++++++++\n\n- Removal of Requests.async in favor of `grequests `_\n- Allow disabling of cookie persistiance.\n- New implementation of safe_mode\n- cookies.get now supports default argument\n- Session cookies not saved when Session.request is called with return_response=False\n- Env: no_proxy support.\n- RequestsCookieJar improvements.\n- Various bug fixes.\n\n0.12.1 (2012-05-08)\n+++++++++++++++++++\n\n- New ``Response.json`` property.\n- Ability to add string file uploads.\n- Fix out-of-range issue with iter_lines.\n- Fix iter_content default size.\n- Fix POST redirects containing files.\n\n0.12.0 (2012-05-02)\n+++++++++++++++++++\n\n- EXPERIMENTAL OAUTH SUPPORT!\n- Proper CookieJar-backed cookies interface with awesome dict-like interface.\n- Speed fix for non-iterated content chunks.\n- Move ``pre_request`` to a more usable place.\n- New ``pre_send`` hook.\n- Lazily encode data, params, files.\n- Load system Certificate Bundle if ``certify`` isn't available.\n- Cleanups, fixes.\n\n0.11.2 (2012-04-22)\n+++++++++++++++++++\n\n- Attempt to use the OS's certificate bundle if ``certifi`` isn't available.\n- Infinite digest auth redirect fix.\n- Multi-part file upload improvements.\n- Fix decoding of invalid %encodings in URLs.\n- If there is no content in a response don't throw an error the second time that content is attempted to be read.\n- Upload data on redirects.\n\n0.11.1 (2012-03-30)\n+++++++++++++++++++\n\n* POST redirects now break RFC to do what browsers do: Follow up with a GET.\n* New ``strict_mode`` configuration to disable new redirect behavior.\n\n\n0.11.0 (2012-03-14)\n+++++++++++++++++++\n\n* Private SSL Certificate support\n* Remove select.poll from Gevent monkeypatching\n* Remove redundant generator for chunked transfer encoding\n* Fix: Response.ok raises Timeout Exception in safe_mode\n\n0.10.8 (2012-03-09)\n+++++++++++++++++++\n\n* Generate chunked ValueError fix\n* Proxy configuration by environment variables\n* Simplification of iter_lines.\n* New `trust_env` configuration for disabling system/environment hints.\n* Suppress cookie errors.\n\n0.10.7 (2012-03-07)\n+++++++++++++++++++\n\n* `encode_uri` = False\n\n0.10.6 (2012-02-25)\n+++++++++++++++++++\n\n* Allow '=' in cookies.\n\n0.10.5 (2012-02-25)\n+++++++++++++++++++\n\n* Response body with 0 content-length fix.\n* New async.imap.\n* Don't fail on netrc.\n\n\n0.10.4 (2012-02-20)\n+++++++++++++++++++\n\n* Honor netrc.\n\n0.10.3 (2012-02-20)\n+++++++++++++++++++\n\n* HEAD requests don't follow redirects anymore.\n* raise_for_status() doesn't raise for 3xx anymore.\n* Make Session objects picklable.\n* ValueError for invalid schema URLs.\n\n0.10.2 (2012-01-15)\n+++++++++++++++++++\n\n* Vastly improved URL quoting.\n* Additional allowed cookie key values.\n* Attempted fix for \"Too many open files\" Error\n* Replace unicode errors on first pass, no need for second pass.\n* Append '/' to bare-domain urls before query insertion.\n* Exceptions now inherit from RuntimeError.\n* Binary uploads + auth fix.\n* Bugfixes.\n\n\n0.10.1 (2012-01-23)\n+++++++++++++++++++\n\n* PYTHON 3 SUPPORT!\n* Dropped 2.5 Support. (*Backwards Incompatible*)\n\n0.10.0 (2012-01-21)\n+++++++++++++++++++\n\n* ``Response.content`` is now bytes-only. (*Backwards Incompatible*)\n* New ``Response.text`` is unicode-only.\n* If no ``Response.encoding`` is specified and ``chardet`` is available, ``Response.text`` will guess an encoding.\n* Default to ISO-8859-1 (Western) encoding for \"text\" subtypes.\n* Removal of `decode_unicode`. (*Backwards Incompatible*)\n* New multiple-hooks system.\n* New ``Response.register_hook`` for registering hooks within the pipeline.\n* ``Response.url`` is now Unicode.\n\n0.9.3 (2012-01-18)\n++++++++++++++++++\n\n* SSL verify=False bugfix (apparent on windows machines).\n\n0.9.2 (2012-01-18)\n++++++++++++++++++\n\n* Asynchronous async.send method.\n* Support for proper chunk streams with boundaries.\n* session argument for Session classes.\n* Print entire hook tracebacks, not just exception instance.\n* Fix response.iter_lines from pending next line.\n* Fix but in HTTP-digest auth w/ URI having query strings.\n* Fix in Event Hooks section.\n* Urllib3 update.\n\n\n0.9.1 (2012-01-06)\n++++++++++++++++++\n\n* danger_mode for automatic Response.raise_for_status()\n* Response.iter_lines refactor\n\n0.9.0 (2011-12-28)\n++++++++++++++++++\n\n* verify ssl is default.\n\n\n0.8.9 (2011-12-28)\n++++++++++++++++++\n\n* Packaging fix.\n\n\n0.8.8 (2011-12-28)\n++++++++++++++++++\n\n* SSL CERT VERIFICATION!\n* Release of Cerifi: Mozilla's cert list.\n* New 'verify' argument for SSL requests.\n* Urllib3 update.\n\n0.8.7 (2011-12-24)\n++++++++++++++++++\n\n* iter_lines last-line truncation fix\n* Force safe_mode for async requests\n* Handle safe_mode exceptions more consistently\n* Fix iteration on null responses in safe_mode\n\n0.8.6 (2011-12-18)\n++++++++++++++++++\n\n* Socket timeout fixes.\n* Proxy Authorization support.\n\n0.8.5 (2011-12-14)\n++++++++++++++++++\n\n* Response.iter_lines!\n\n0.8.4 (2011-12-11)\n++++++++++++++++++\n\n* Prefetch bugfix.\n* Added license to installed version.\n\n0.8.3 (2011-11-27)\n++++++++++++++++++\n\n* Converted auth system to use simpler callable objects.\n* New session parameter to API methods.\n* Display full URL while logging.\n\n0.8.2 (2011-11-19)\n++++++++++++++++++\n\n* New Unicode decoding system, based on over-ridable `Response.encoding`.\n* Proper URL slash-quote handling.\n* Cookies with ``[``, ``]``, and ``_`` allowed.\n\n0.8.1 (2011-11-15)\n++++++++++++++++++\n\n* URL Request path fix\n* Proxy fix.\n* Timeouts fix.\n\n0.8.0 (2011-11-13)\n++++++++++++++++++\n\n* Keep-alive support!\n* Complete removal of Urllib2\n* Complete removal of Poster\n* Complete removal of CookieJars\n* New ConnectionError raising\n* Safe_mode for error catching\n* prefetch parameter for request methods\n* OPTION method\n* Async pool size throttling\n* File uploads send real names\n* Vendored in urllib3\n\n0.7.6 (2011-11-07)\n++++++++++++++++++\n\n* Digest authentication bugfix (attach query data to path)\n\n0.7.5 (2011-11-04)\n++++++++++++++++++\n\n* Response.content = None if there was an invalid response.\n* Redirection auth handling.\n\n0.7.4 (2011-10-26)\n++++++++++++++++++\n\n* Session Hooks fix.\n\n0.7.3 (2011-10-23)\n++++++++++++++++++\n\n* Digest Auth fix.\n\n\n0.7.2 (2011-10-23)\n++++++++++++++++++\n\n* PATCH Fix.\n\n\n0.7.1 (2011-10-23)\n++++++++++++++++++\n\n* Move away from urllib2 authentication handling.\n* Fully Remove AuthManager, AuthObject, &c.\n* New tuple-based auth system with handler callbacks.\n\n\n0.7.0 (2011-10-22)\n++++++++++++++++++\n\n* Sessions are now the primary interface.\n* Deprecated InvalidMethodException.\n* PATCH fix.\n* New config system (no more global settings).\n\n\n0.6.6 (2011-10-19)\n++++++++++++++++++\n\n* Session parameter bugfix (params merging).\n\n\n0.6.5 (2011-10-18)\n++++++++++++++++++\n\n* Offline (fast) test suite.\n* Session dictionary argument merging.\n\n\n0.6.4 (2011-10-13)\n++++++++++++++++++\n\n* Automatic decoding of unicode, based on HTTP Headers.\n* New ``decode_unicode`` setting.\n* Removal of ``r.read/close`` methods.\n* New ``r.faw`` interface for advanced response usage.*\n* Automatic expansion of parameterized headers.\n\n\n0.6.3 (2011-10-13)\n++++++++++++++++++\n\n* Beautiful ``requests.async`` module, for making async requests w/ gevent.\n\n\n0.6.2 (2011-10-09)\n++++++++++++++++++\n\n* GET/HEAD obeys allow_redirects=False.\n\n\n0.6.1 (2011-08-20)\n++++++++++++++++++\n\n* Enhanced status codes experience ``\\o/``\n* Set a maximum number of redirects (``settings.max_redirects``)\n* Full Unicode URL support\n* Support for protocol-less redirects.\n* Allow for arbitrary request types.\n* Bugfixes\n\n\n0.6.0 (2011-08-17)\n++++++++++++++++++\n\n* New callback hook system\n* New persistent sessions object and context manager\n* Transparent Dict-cookie handling\n* Status code reference object\n* Removed Response.cached\n* Added Response.request\n* All args are kwargs\n* Relative redirect support\n* HTTPError handling improvements\n* Improved https testing\n* Bugfixes\n\n\n0.5.1 (2011-07-23)\n++++++++++++++++++\n\n* International Domain Name Support!\n* Access headers without fetching entire body (``read()``)\n* Use lists as dicts for parameters\n* Add Forced Basic Authentication\n* Forced Basic is default authentication type\n* ``python-requests.org`` default User-Agent header\n* CaseInsensitiveDict lower-case caching\n* Response.history bugfix\n\n\n0.5.0 (2011-06-21)\n++++++++++++++++++\n\n* PATCH Support\n* Support for Proxies\n* HTTPBin Test Suite\n* Redirect Fixes\n* settings.verbose stream writing\n* Querystrings for all methods\n* URLErrors (Connection Refused, Timeout, Invalid URLs) are treated as explicitly raised\n ``r.requests.get('hwe://blah'); r.raise_for_status()``\n\n\n0.4.1 (2011-05-22)\n++++++++++++++++++\n\n* Improved Redirection Handling\n* New 'allow_redirects' param for following non-GET/HEAD Redirects\n* Settings module refactoring\n\n\n0.4.0 (2011-05-15)\n++++++++++++++++++\n\n* Response.history: list of redirected responses\n* Case-Insensitive Header Dictionaries!\n* Unicode URLs\n\n\n0.3.4 (2011-05-14)\n++++++++++++++++++\n\n* Urllib2 HTTPAuthentication Recursion fix (Basic/Digest)\n* Internal Refactor\n* Bytes data upload Bugfix\n\n\n\n0.3.3 (2011-05-12)\n++++++++++++++++++\n\n* Request timeouts\n* Unicode url-encoded data\n* Settings context manager and module\n\n\n0.3.2 (2011-04-15)\n++++++++++++++++++\n\n* Automatic Decompression of GZip Encoded Content\n* AutoAuth Support for Tupled HTTP Auth\n\n\n0.3.1 (2011-04-01)\n++++++++++++++++++\n\n* Cookie Changes\n* Response.read()\n* Poster fix\n\n\n0.3.0 (2011-02-25)\n++++++++++++++++++\n\n* Automatic Authentication API Change\n* Smarter Query URL Parameterization\n* Allow file uploads and POST data together\n* New Authentication Manager System\n - Simpler Basic HTTP System\n - Supports all build-in urllib2 Auths\n - Allows for custom Auth Handlers\n\n\n0.2.4 (2011-02-19)\n++++++++++++++++++\n\n* Python 2.5 Support\n* PyPy-c v1.4 Support\n* Auto-Authentication tests\n* Improved Request object constructor\n\n0.2.3 (2011-02-15)\n++++++++++++++++++\n\n* New HTTPHandling Methods\n - Response.__nonzero__ (false if bad HTTP Status)\n - Response.ok (True if expected HTTP Status)\n - Response.error (Logged HTTPError if bad HTTP Status)\n - Response.raise_for_status() (Raises stored HTTPError)\n\n\n0.2.2 (2011-02-14)\n++++++++++++++++++\n\n* Still handles request in the event of an HTTPError. (Issue #2)\n* Eventlet and Gevent Monkeypatch support.\n* Cookie Support (Issue #1)\n\n\n0.2.1 (2011-02-14)\n++++++++++++++++++\n\n* Added file attribute to POST and PUT requests for multipart-encode file uploads.\n* Added Request.url attribute for context and redirects\n\n\n0.2.0 (2011-02-14)\n++++++++++++++++++\n\n* Birth!\n\n\n0.0.1 (2011-02-13)\n++++++++++++++++++\n\n* Frustration\n* Conception",
"release_url": "http://pypi.python.org/pypi/requests/2.8.1",
"downloads": {
"last_month": 5397123,
"last_week": 1226167,
"last_day": 232917
},
"_pypi_ordering": 98,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Natural Language :: English",
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5"
],
"name": "requests",
"bugtrack_url": null,
"license": "Apache 2.0",
"summary": "Python HTTP for Humans.",
"home_page": "http://python-requests.org",
"cheesecake_installability_id": null
},
"releases": {
"1.0.4": [
{
"has_sig": false,
"upload_time": "2012-12-23T07:45:10",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.0.4.tar.gz",
"md5_digest": "0b7448f9e1a077a7218720575003a1b6",
"downloads": 112618,
"filename": "requests-1.0.4.tar.gz",
"packagetype": "sdist",
"size": 336280
}
],
"1.0.0": [
{
"has_sig": false,
"upload_time": "2012-12-17T15:00:05",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.0.0.tar.gz",
"md5_digest": "099c9035c4b30a7ae5484b1beabc7407",
"downloads": 16077,
"filename": "requests-1.0.0.tar.gz",
"packagetype": "sdist",
"size": 335548
}
],
"1.0.1": [
{
"has_sig": false,
"upload_time": "2012-12-17T18:53:51",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.0.1.tar.gz",
"md5_digest": "2e938f26f2bdf2899862c751bfa7eff5",
"downloads": 3679,
"filename": "requests-1.0.1.tar.gz",
"packagetype": "sdist",
"size": 335625
}
],
"1.0.2": [
{
"has_sig": false,
"upload_time": "2012-12-17T19:04:31",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.0.2.tar.gz",
"md5_digest": "e5c1a5a5472cd61f144743dd25a2a29f",
"downloads": 6921,
"filename": "requests-1.0.2.tar.gz",
"packagetype": "sdist",
"size": 335653
}
],
"1.0.3": [
{
"has_sig": false,
"upload_time": "2012-12-18T09:51:12",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.0.3.tar.gz",
"md5_digest": "a3169a33973d4b5b51843ead01c5e999",
"downloads": 51762,
"filename": "requests-1.0.3.tar.gz",
"packagetype": "sdist",
"size": 335757
}
],
"0.3.2": [
{
"has_sig": false,
"upload_time": "2011-04-15T23:30:50",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.3.2.tar.gz",
"md5_digest": "bde777f4c5b7bbb09033901c443962b3",
"downloads": 5216,
"filename": "requests-0.3.2.tar.gz",
"packagetype": "sdist",
"size": 15515
}
],
"0.3.3": [
{
"has_sig": false,
"upload_time": "2011-05-12T10:03:24",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.3.3.tar.gz",
"md5_digest": "84c762c116617ba4dd03c19e2b61eb53",
"downloads": 4934,
"filename": "requests-0.3.3.tar.gz",
"packagetype": "sdist",
"size": 18995
}
],
"0.3.0": [
{
"has_sig": false,
"upload_time": "2011-02-25T14:58:38",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.3.0.tar.gz",
"md5_digest": "aa1306575a78ba8b5e625dd2645d2ef0",
"downloads": 5249,
"filename": "requests-0.3.0.tar.gz",
"packagetype": "sdist",
"size": 15021
}
],
"0.3.1": [
{
"has_sig": false,
"upload_time": "2011-04-01T20:55:03",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.3.1.tar.gz",
"md5_digest": "3f4701e2ab414cd7018804a70328c527",
"downloads": 5143,
"filename": "requests-0.3.1.tar.gz",
"packagetype": "sdist",
"size": 15275
}
],
"0.3.4": [
{
"has_sig": false,
"upload_time": "2011-05-14T20:30:44",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.3.4.tar.gz",
"md5_digest": "55152cc2b135bc8989dc4fa279295f8b",
"downloads": 4894,
"filename": "requests-0.3.4.tar.gz",
"packagetype": "sdist",
"size": 19773
}
],
"0.12.01": [],
"2.8.1": [
{
"has_sig": false,
"upload_time": "2015-10-13T12:56:41",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.8.1-py2.py3-none-any.whl",
"md5_digest": "46f1d621daa3ab38958a42f51478b1ee",
"downloads": 3302171,
"filename": "requests-2.8.1-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 497953
},
{
"has_sig": false,
"upload_time": "2015-10-13T12:56:34",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.8.1.tar.gz",
"md5_digest": "a27ea3d72d7822906ddce5e252d6add9",
"downloads": 2462803,
"filename": "requests-2.8.1.tar.gz",
"packagetype": "sdist",
"size": 480803
}
],
"2.8.0": [
{
"has_sig": false,
"upload_time": "2015-10-06T14:47:57",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.8.0-py2.py3-none-any.whl",
"md5_digest": "52236eb6f886db4d2afba43775c97050",
"downloads": 416112,
"filename": "requests-2.8.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 476582
},
{
"has_sig": false,
"upload_time": "2015-10-06T14:48:08",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.8.0.tar.gz",
"md5_digest": "3ec7198fc935d83c3eacff1ed4095ce4",
"downloads": 388624,
"filename": "requests-2.8.0.tar.gz",
"packagetype": "sdist",
"size": 457879
}
],
"0.6.5": [
{
"has_sig": false,
"upload_time": "2011-10-19T07:30:59",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.6.5.tar.gz",
"md5_digest": "52f8bc956e027c8a0eb2684f6928169d",
"downloads": 4778,
"filename": "requests-0.6.5.tar.gz",
"packagetype": "sdist",
"size": 30647
}
],
"2.0.1": [
{
"has_sig": false,
"upload_time": "2013-11-15T19:12:20",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.0.1-py2.py3-none-any.whl",
"md5_digest": "d524f9a38a29efe1732fd130e5ebe433",
"downloads": 316311,
"filename": "requests-2.0.1-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 439330
},
{
"has_sig": false,
"upload_time": "2013-10-24T14:33:21",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.0.1.tar.gz",
"md5_digest": "38e61c2856d2ba2782286730241975e6",
"downloads": 1450321,
"filename": "requests-2.0.1.tar.gz",
"packagetype": "sdist",
"size": 412648
}
],
"2.0.0": [
{
"has_sig": false,
"upload_time": "2013-11-15T19:09:51",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.0.0-py2.py3-none-any.whl",
"md5_digest": "6af9c16dbddd2fc751ae4f1606d041e8",
"downloads": 299730,
"filename": "requests-2.0.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 391141
},
{
"has_sig": false,
"upload_time": "2013-09-24T18:39:33",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.0.0.tar.gz",
"md5_digest": "856fc825c17483e25fd55db115028e3f",
"downloads": 1063100,
"filename": "requests-2.0.0.tar.gz",
"packagetype": "sdist",
"size": 362994
}
],
"0.6.6": [
{
"has_sig": false,
"upload_time": "2011-10-19T09:39:56",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.6.6.tar.gz",
"md5_digest": "2180dacebc0e30ba730d083739907af6",
"downloads": 6671,
"filename": "requests-0.6.6.tar.gz",
"packagetype": "sdist",
"size": 30809
}
],
"2.2.1": [
{
"has_sig": false,
"upload_time": "2014-01-23T18:26:15",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.2.1-py2.py3-none-any.whl",
"md5_digest": "1e38addb978e50bd86f62bda53956b03",
"downloads": 2658147,
"filename": "requests-2.2.1-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 625382
},
{
"has_sig": false,
"upload_time": "2014-01-23T18:26:12",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.2.1.tar.gz",
"md5_digest": "ac27081135f58d1a43e4fb38258d6f4e",
"downloads": 3407542,
"filename": "requests-2.2.1.tar.gz",
"packagetype": "sdist",
"size": 421978
}
],
"2.2.0": [
{
"has_sig": false,
"upload_time": "2014-01-09T19:33:37",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.2.0-py2.py3-none-any.whl",
"md5_digest": "8f989615bb0d276d5f3158e7efab494c",
"downloads": 144823,
"filename": "requests-2.2.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 623932
},
{
"has_sig": false,
"upload_time": "2014-01-09T19:33:32",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.2.0.tar.gz",
"md5_digest": "4d2e17221d478ece045e2e81cdb177f5",
"downloads": 392673,
"filename": "requests-2.2.0.tar.gz",
"packagetype": "sdist",
"size": 421997
}
],
"0.6.3": [
{
"has_sig": false,
"upload_time": "2011-10-14T03:35:13",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.6.3.tar.gz",
"md5_digest": "35a954ae85b358e498fb0e602f1dce9d",
"downloads": 4769,
"filename": "requests-0.6.3.tar.gz",
"packagetype": "sdist",
"size": 26606
}
],
"0.6.2": [
{
"has_sig": false,
"upload_time": "2011-10-09T13:12:45",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.6.2.tar.gz",
"md5_digest": "0583bb5393b9cfcb022dc2aef7d6ffc8",
"downloads": 7850,
"filename": "requests-0.6.2.tar.gz",
"packagetype": "sdist",
"size": 26524
}
],
"0.6.1": [
{
"has_sig": false,
"upload_time": "2011-08-21T00:25:37",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.6.1.tar.gz",
"md5_digest": "07770334d48bd69ede1cc28cd0dd7680",
"downloads": 21399,
"filename": "requests-0.6.1.tar.gz",
"packagetype": "sdist",
"size": 26107
}
],
"0.6.0": [
{
"has_sig": false,
"upload_time": "2011-08-17T10:33:05",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.6.0.tar.gz",
"md5_digest": "235e9fb6bfd71a48c0f00c0d5aef8896",
"downloads": 6029,
"filename": "requests-0.6.0.tar.gz",
"packagetype": "sdist",
"size": 25692
}
],
"0.11.1": [
{
"has_sig": false,
"upload_time": "2012-03-31T05:47:56",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.11.1.tar.gz",
"md5_digest": "c903c32a0e1f04889e693da8e9c71872",
"downloads": 38150,
"filename": "requests-0.11.1.tar.gz",
"packagetype": "sdist",
"size": 63100
}
],
"0.11.2": [
{
"has_sig": false,
"upload_time": "2012-04-23T04:29:36",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.11.2.tar.gz",
"md5_digest": "5acd23600c897bf1560dca18005b428c",
"downloads": 36476,
"filename": "requests-0.11.2.tar.gz",
"packagetype": "sdist",
"size": 71080
}
],
"0.6.4": [
{
"has_sig": false,
"upload_time": "2011-10-14T04:23:31",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.6.4.tar.gz",
"md5_digest": "e0eec314178ad9a7bb14f2ec32f35ba3",
"downloads": 9277,
"filename": "requests-0.6.4.tar.gz",
"packagetype": "sdist",
"size": 30212
}
],
"0.13.8": [
{
"has_sig": false,
"upload_time": "2012-08-20T15:24:42",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.8.tar.gz",
"md5_digest": "d01596bd344db94763b2e4dfaa7bc7b9",
"downloads": 26725,
"filename": "requests-0.13.8.tar.gz",
"packagetype": "sdist",
"size": 522140
}
],
"0.13.9": [
{
"has_sig": false,
"upload_time": "2012-08-25T15:26:50",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.9.tar.gz",
"md5_digest": "66d52b8f47be517fc91a6e18d6b9ce82",
"downloads": 436286,
"filename": "requests-0.13.9.tar.gz",
"packagetype": "sdist",
"size": 522477
}
],
"0.13.6": [
{
"has_sig": false,
"upload_time": "2012-08-06T08:46:22",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.6.tar.gz",
"md5_digest": "9ea0f38cc4bf444be5a4c90f127211f2",
"downloads": 83586,
"filename": "requests-0.13.6.tar.gz",
"packagetype": "sdist",
"size": 520031
}
],
"0.13.7": [
{
"has_sig": false,
"upload_time": "2012-08-19T00:47:48",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.7.tar.gz",
"md5_digest": "9212044f915d44fe3010cb923c0e08e5",
"downloads": 20167,
"filename": "requests-0.13.7.tar.gz",
"packagetype": "sdist",
"size": 521660
}
],
"0.13.4": [
{
"has_sig": false,
"upload_time": "2012-07-27T08:22:09",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.4.tar.gz",
"md5_digest": "286cd3352509691e81c520accc5b9e48",
"downloads": 4033,
"filename": "requests-0.13.4.tar.gz",
"packagetype": "sdist",
"size": 519515
}
],
"0.13.5": [
{
"has_sig": false,
"upload_time": "2012-07-27T09:23:41",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.5.tar.gz",
"md5_digest": "805fd122b4cfd224e15ff2f5288c5ba0",
"downloads": 89067,
"filename": "requests-0.13.5.tar.gz",
"packagetype": "sdist",
"size": 519553
}
],
"0.13.2": [
{
"has_sig": false,
"upload_time": "2012-06-29T02:37:41",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.2.tar.gz",
"md5_digest": "fac5635391778e2394a411d37e69ae5e",
"downloads": 117591,
"filename": "requests-0.13.2.tar.gz",
"packagetype": "sdist",
"size": 514484
}
],
"0.13.3": [
{
"has_sig": false,
"upload_time": "2012-07-12T23:20:43",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.3.tar.gz",
"md5_digest": "54387d7df6c69580b906dcb5a2bd0724",
"downloads": 144912,
"filename": "requests-0.13.3.tar.gz",
"packagetype": "sdist",
"size": 515192
}
],
"0.13.0": [
{
"has_sig": false,
"upload_time": "2012-05-30T02:54:18",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.0.tar.gz",
"md5_digest": "7d41e51c273806456faab61370d5147e",
"downloads": 40543,
"filename": "requests-0.13.0.tar.gz",
"packagetype": "sdist",
"size": 68172
}
],
"0.13.1": [
{
"has_sig": false,
"upload_time": "2012-06-08T04:22:28",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.13.1.tar.gz",
"md5_digest": "31a08091feeefe60817e45122d933219",
"downloads": 121874,
"filename": "requests-0.13.1.tar.gz",
"packagetype": "sdist",
"size": 68474
}
],
"0.8.9": [
{
"has_sig": false,
"upload_time": "2011-12-28T10:34:17",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.9.tar.gz",
"md5_digest": "ff5b3bf5bc3ad19930d3f3afe51f182b",
"downloads": 10963,
"filename": "requests-0.8.9.tar.gz",
"packagetype": "sdist",
"size": 55153
}
],
"0.8.8": [
{
"has_sig": false,
"upload_time": "2011-12-28T09:55:45",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.8.tar.gz",
"md5_digest": "bfb182cfd3ed839b97744c553b87f502",
"downloads": 5368,
"filename": "requests-0.8.8.tar.gz",
"packagetype": "sdist",
"size": 54212
}
],
"0.14.2": [
{
"has_sig": false,
"upload_time": "2012-10-27T15:08:51",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.14.2.tar.gz",
"md5_digest": "488508ba3e8270992ad5b3fb54d364ca",
"downloads": 918155,
"filename": "requests-0.14.2.tar.gz",
"packagetype": "sdist",
"size": 361488
}
],
"0.8.5": [
{
"has_sig": false,
"upload_time": "2011-12-14T16:43:21",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.5.tar.gz",
"md5_digest": "5f2975ee9e57f4ea000e5a3f50fc85d1",
"downloads": 6281,
"filename": "requests-0.8.5.tar.gz",
"packagetype": "sdist",
"size": 52351
}
],
"0.8.4": [
{
"has_sig": false,
"upload_time": "2011-12-11T17:40:28",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.4.tar.gz",
"md5_digest": "642e5c70250989e4feda9c50be57b100",
"downloads": 13119,
"filename": "requests-0.8.4.tar.gz",
"packagetype": "sdist",
"size": 52100
}
],
"0.8.7": [
{
"has_sig": false,
"upload_time": "2011-12-24T09:18:54",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.7.tar.gz",
"md5_digest": "e4d4ee3a90396908bd04b50bf2136617",
"downloads": 5369,
"filename": "requests-0.8.7.tar.gz",
"packagetype": "sdist",
"size": 53578
}
],
"0.8.6": [
{
"has_sig": false,
"upload_time": "2011-12-19T01:18:29",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.6.tar.gz",
"md5_digest": "21b03926ab38417a704ebce57972571a",
"downloads": 7981,
"filename": "requests-0.8.6.tar.gz",
"packagetype": "sdist",
"size": 52670
}
],
"0.8.1": [
{
"has_sig": false,
"upload_time": "2011-11-15T16:01:47",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.1.tar.gz",
"md5_digest": "6135f837fbd113fc62904c60dcc5c70d",
"downloads": 8542,
"filename": "requests-0.8.1.tar.gz",
"packagetype": "sdist",
"size": 39046
}
],
"0.8.0": [
{
"has_sig": false,
"upload_time": "2011-11-13T06:52:10",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.0.tar.gz",
"md5_digest": "64dc0095cb645aa7f0083957950d524d",
"downloads": 5776,
"filename": "requests-0.8.0.tar.gz",
"packagetype": "sdist",
"size": 38785
}
],
"0.8.3": [
{
"has_sig": false,
"upload_time": "2011-11-27T16:44:51",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.3.tar.gz",
"md5_digest": "93e4cd27ab646fb613a926fede1cc4f5",
"downloads": 12461,
"filename": "requests-0.8.3.tar.gz",
"packagetype": "sdist",
"size": 51252
}
],
"0.8.2": [
{
"has_sig": false,
"upload_time": "2011-11-19T22:28:31",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.8.2.tar.gz",
"md5_digest": "bdbbd7f45688e23e87eec52835959943",
"downloads": 30687,
"filename": "requests-0.8.2.tar.gz",
"packagetype": "sdist",
"size": 51162
}
],
"0.4.1": [
{
"has_sig": false,
"upload_time": "2011-05-25T18:54:05",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.4.1.tar.gz",
"md5_digest": "812ff0ce63d14f7b940bacd880d54ee0",
"downloads": 9781,
"filename": "requests-0.4.1.tar.gz",
"packagetype": "sdist",
"size": 18443
}
],
"0.4.0": [
{
"has_sig": false,
"upload_time": "2011-05-15T05:58:43",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.4.0.tar.gz",
"md5_digest": "77a7a7edd54169c6fa7ace49dcb0b20c",
"downloads": 5713,
"filename": "requests-0.4.0.tar.gz",
"packagetype": "sdist",
"size": 17194
}
],
"2.4.3": [
{
"has_sig": false,
"upload_time": "2014-10-06T09:44:49",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.4.3-py2.py3-none-any.whl",
"md5_digest": "0a66a9c4c22272680430fbb9fb4ca34f",
"downloads": 2501282,
"filename": "requests-2.4.3-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 459464
},
{
"has_sig": false,
"upload_time": "2014-10-06T09:44:44",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.4.3.tar.gz",
"md5_digest": "02214b3a179e445545de4b7a98d3dd17",
"downloads": 2827852,
"filename": "requests-2.4.3.tar.gz",
"packagetype": "sdist",
"size": 438132
}
],
"2.4.2": [
{
"has_sig": false,
"upload_time": "2014-10-05T17:15:53",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.4.2-py2.py3-none-any.whl",
"md5_digest": "f49f34b1fcdef6b557964deea1a80cf3",
"downloads": 26784,
"filename": "requests-2.4.2-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 459326
},
{
"has_sig": false,
"upload_time": "2014-10-05T17:15:45",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.4.2.tar.gz",
"md5_digest": "a2476d2dd83a0520847f216ce0b5f9d1",
"downloads": 46933,
"filename": "requests-2.4.2.tar.gz",
"packagetype": "sdist",
"size": 437898
}
],
"2.4.1": [
{
"has_sig": false,
"upload_time": "2014-09-09T16:35:12",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.4.1-py2.py3-none-any.whl",
"md5_digest": "19d5413dc71309e4fb1f8103b8eb99ce",
"downloads": 921215,
"filename": "requests-2.4.1-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 458354
},
{
"has_sig": false,
"upload_time": "2014-09-09T16:35:08",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.4.1.tar.gz",
"md5_digest": "931461f761c70708c46ea65b7889da58",
"downloads": 1352590,
"filename": "requests-2.4.1.tar.gz",
"packagetype": "sdist",
"size": 436872
}
],
"2.4.0": [
{
"has_sig": false,
"upload_time": "2014-08-29T14:32:48",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.4.0-py2.py3-none-any.whl",
"md5_digest": "47948d2fb3f2aa04235e6f637814b226",
"downloads": 308274,
"filename": "requests-2.4.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 457810
},
{
"has_sig": false,
"upload_time": "2014-08-29T14:32:45",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.4.0.tar.gz",
"md5_digest": "99b830d1afe2e5920adbea0fe3120948",
"downloads": 597502,
"filename": "requests-2.4.0.tar.gz",
"packagetype": "sdist",
"size": 436334
}
],
"2.6.1": [
{
"has_sig": false,
"upload_time": "2015-04-23T02:27:04",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.6.1-py2.py3-none-any.whl",
"md5_digest": "adb8e91b3367bc0417ef1e4a6dced9b1",
"downloads": 47681,
"filename": "requests-2.6.1-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 469962
},
{
"has_sig": false,
"upload_time": "2015-04-23T02:27:12",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.6.1.tar.gz",
"md5_digest": "da6e487f89e6a531699b7fd97ff182af",
"downloads": 43134,
"filename": "requests-2.6.1.tar.gz",
"packagetype": "sdist",
"size": 450975
}
],
"2.6.0": [
{
"has_sig": false,
"upload_time": "2015-03-14T16:44:37",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.6.0-py2.py3-none-any.whl",
"md5_digest": "3ab1972bbaf2802d94516fb86b9b0d0b",
"downloads": 2003507,
"filename": "requests-2.6.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 469802
},
{
"has_sig": false,
"upload_time": "2015-03-14T16:44:48",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.6.0.tar.gz",
"md5_digest": "25287278fa3ea106207461112bb37050",
"downloads": 2402265,
"filename": "requests-2.6.0.tar.gz",
"packagetype": "sdist",
"size": 450389
}
],
"2.6.2": [
{
"has_sig": false,
"upload_time": "2015-04-23T16:30:52",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.6.2-py2.py3-none-any.whl",
"md5_digest": "36746c275589b2154307bbcc6d28320a",
"downloads": 839011,
"filename": "requests-2.6.2-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 470140
},
{
"has_sig": false,
"upload_time": "2015-04-23T16:31:01",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.6.2.tar.gz",
"md5_digest": "0d703e5be558566e0f8c37f960d95372",
"downloads": 598600,
"filename": "requests-2.6.2.tar.gz",
"packagetype": "sdist",
"size": 451109
}
],
"1.1.0": [
{
"has_sig": false,
"upload_time": "2013-01-10T07:13:41",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.1.0.tar.gz",
"md5_digest": "a0158815af244c32041a3147ee09abf3",
"downloads": 1423824,
"filename": "requests-1.1.0.tar.gz",
"packagetype": "sdist",
"size": 337229
}
],
"0.2.4": [
{
"has_sig": false,
"upload_time": "2011-02-19T07:03:14",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.2.4.tar.gz",
"md5_digest": "62dbe8cf12bc1ccd03776e74f59e9ef6",
"downloads": 5627,
"filename": "requests-0.2.4.tar.gz",
"packagetype": "sdist",
"size": 13653
}
],
"0.2.3": [
{
"has_sig": false,
"upload_time": "2011-02-15T15:47:29",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.2.3.tar.gz",
"md5_digest": "102243646fc0cffdc82269f4bb5c6d5d",
"downloads": 5009,
"filename": "requests-0.2.3.tar.gz",
"packagetype": "sdist",
"size": 13255
}
],
"0.2.2": [
{
"has_sig": false,
"upload_time": "2011-02-14T18:58:40",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.2.2.tar.gz",
"md5_digest": "a703489b1a4a650698ddcf84857360c6",
"downloads": 4960,
"filename": "requests-0.2.2.tar.gz",
"packagetype": "sdist",
"size": 13049
}
],
"0.2.1": [
{
"has_sig": false,
"upload_time": "2011-02-14T16:38:12",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.2.1.tar.gz",
"md5_digest": "7e9590f3985ece46fc8306e906b458c7",
"downloads": 4947,
"filename": "requests-0.2.1.tar.gz",
"packagetype": "sdist",
"size": 12715
}
],
"0.2.0": [
{
"has_sig": false,
"upload_time": "2011-02-14T08:49:42",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.2.0.tar.gz",
"md5_digest": "637ae94cb6f2f1d9ea9020293055964a",
"downloads": 5066,
"filename": "requests-0.2.0.tar.gz",
"packagetype": "sdist",
"size": 5533
}
],
"0.14.1": [
{
"has_sig": false,
"upload_time": "2012-10-01T17:30:05",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.14.1.tar.gz",
"md5_digest": "3de30600072cbc7214ae342d1d08aa46",
"downloads": 299590,
"filename": "requests-0.14.1.tar.gz",
"packagetype": "sdist",
"size": 523254
}
],
"2.1.0": [
{
"has_sig": false,
"upload_time": "2013-12-05T22:51:41",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.1.0-py2.py3-none-any.whl",
"md5_digest": "0848cbc0cc7edd150cb8d6ddc25ca906",
"downloads": 802779,
"filename": "requests-2.1.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 445280
},
{
"has_sig": false,
"upload_time": "2013-12-05T22:51:38",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.1.0.tar.gz",
"md5_digest": "28543001831f46b1ff40686ebc027deb",
"downloads": 980428,
"filename": "requests-2.1.0.tar.gz",
"packagetype": "sdist",
"size": 420289
}
],
"0.14.0": [
{
"has_sig": false,
"upload_time": "2012-09-02T08:50:39",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.14.0.tar.gz",
"md5_digest": "a809c747e4f09b92147721ebc3e23dd6",
"downloads": 502482,
"filename": "requests-0.14.0.tar.gz",
"packagetype": "sdist",
"size": 523133
}
],
"0.10.1": [
{
"has_sig": false,
"upload_time": "2012-01-23T08:22:52",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.1.tar.gz",
"md5_digest": "699147d2143bff95238befa58980b912",
"downloads": 26671,
"filename": "requests-0.10.1.tar.gz",
"packagetype": "sdist",
"size": 63234
}
],
"0.10.0": [
{
"has_sig": false,
"upload_time": "2012-01-22T05:08:17",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.0.tar.gz",
"md5_digest": "c90a48af18eb4170dbe4832c1104440c",
"downloads": 10399,
"filename": "requests-0.10.0.tar.gz",
"packagetype": "sdist",
"size": 62046
}
],
"0.10.3": [
{
"has_sig": false,
"upload_time": "2012-02-20T20:10:57",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.3.tar.gz",
"md5_digest": "a055af00593f4828c3becd0ccfab503f",
"downloads": 4709,
"filename": "requests-0.10.3.tar.gz",
"packagetype": "sdist",
"size": 60493
}
],
"0.10.2": [
{
"has_sig": false,
"upload_time": "2012-02-15T09:48:52",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.2.tar.gz",
"md5_digest": "47c3cf85a0112d423137b43989663bef",
"downloads": 17350,
"filename": "requests-0.10.2.tar.gz",
"packagetype": "sdist",
"size": 60158
}
],
"0.0.1": [],
"0.10.4": [
{
"has_sig": false,
"upload_time": "2012-02-20T22:21:31",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.4.tar.gz",
"md5_digest": "5e465e9e739bcc9f71935ca4e9706168",
"downloads": 13489,
"filename": "requests-0.10.4.tar.gz",
"packagetype": "sdist",
"size": 60889
}
],
"0.10.7": [
{
"has_sig": false,
"upload_time": "2012-03-08T01:50:58",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.7.tar.gz",
"md5_digest": "a3ac9d431981dcfd592fd0f35c499e4a",
"downloads": 6757,
"filename": "requests-0.10.7.tar.gz",
"packagetype": "sdist",
"size": 61826
}
],
"0.10.6": [
{
"has_sig": false,
"upload_time": "2012-02-26T05:17:54",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.6.tar.gz",
"md5_digest": "c889401445de3cbbac98509208a73b83",
"downloads": 28073,
"filename": "requests-0.10.6.tar.gz",
"packagetype": "sdist",
"size": 61673
}
],
"0.7.6": [
{
"has_sig": false,
"upload_time": "2011-11-07T20:19:31",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.7.6.tar.gz",
"md5_digest": "728b21bf3914d69a4ff1012c66d9b6ba",
"downloads": 8941,
"filename": "requests-0.7.6.tar.gz",
"packagetype": "sdist",
"size": 32748
}
],
"0.10.8": [
{
"has_sig": false,
"upload_time": "2012-03-09T17:59:54",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.10.8.tar.gz",
"md5_digest": "0fc89a30eef76b2393cbc7ebace91750",
"downloads": 97221,
"filename": "requests-0.10.8.tar.gz",
"packagetype": "sdist",
"size": 62201
}
],
"0.7.4": [
{
"has_sig": false,
"upload_time": "2011-10-27T00:36:25",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.7.4.tar.gz",
"md5_digest": "c015765399b8c1e309c84ade0d38f07b",
"downloads": 10308,
"filename": "requests-0.7.4.tar.gz",
"packagetype": "sdist",
"size": 31873
}
],
"0.7.5": [
{
"has_sig": false,
"upload_time": "2011-11-05T04:32:37",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.7.5.tar.gz",
"md5_digest": "9a12281a811ca25d347d806c456d96f1",
"downloads": 5899,
"filename": "requests-0.7.5.tar.gz",
"packagetype": "sdist",
"size": 32298
}
],
"0.7.2": [
{
"has_sig": false,
"upload_time": "2011-10-23T21:40:37",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.7.2.tar.gz",
"md5_digest": "03eb97ed6aacb4102fd434bbfc13ce17",
"downloads": 5630,
"filename": "requests-0.7.2.tar.gz",
"packagetype": "sdist",
"size": 31837
}
],
"0.7.3": [
{
"has_sig": false,
"upload_time": "2011-10-23T23:04:13",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.7.3.tar.gz",
"md5_digest": "267f6f7d1109775d24a288f798e3ab4a",
"downloads": 9194,
"filename": "requests-0.7.3.tar.gz",
"packagetype": "sdist",
"size": 31805
}
],
"0.7.0": [
{
"has_sig": false,
"upload_time": "2011-10-23T03:33:24",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.7.0.tar.gz",
"md5_digest": "83a1a7d79218756efd19b254eeb6b1f0",
"downloads": 4993,
"filename": "requests-0.7.0.tar.gz",
"packagetype": "sdist",
"size": 31260
}
],
"0.7.1": [
{
"has_sig": false,
"upload_time": "2011-10-23T21:19:22",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.7.1.tar.gz",
"md5_digest": "4821c6902d8e83c910c69c6492388e5f",
"downloads": 4675,
"filename": "requests-0.7.1.tar.gz",
"packagetype": "sdist",
"size": 31804
}
],
"0.12.1": [
{
"has_sig": false,
"upload_time": "2012-05-08T07:21:59",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.12.1.tar.gz",
"md5_digest": "fe9e0515d09733d0eb9e2031c03401b2",
"downloads": 131145,
"filename": "requests-0.12.1.tar.gz",
"packagetype": "sdist",
"size": 78245
}
],
"0.12.0": [
{
"has_sig": false,
"upload_time": "2012-05-03T01:18:47",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.12.0.tar.gz",
"md5_digest": "c38bacf4d6a065f3c47463e63efdfb5a",
"downloads": 12488,
"filename": "requests-0.12.0.tar.gz",
"packagetype": "sdist",
"size": 76859
}
],
"0.5.0": [
{
"has_sig": false,
"upload_time": "2011-06-22T04:44:39",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.5.0.tar.gz",
"md5_digest": "6dfdc1688217d774d524e056ec6605a6",
"downloads": 8450,
"filename": "requests-0.5.0.tar.gz",
"packagetype": "sdist",
"size": 21945
}
],
"0.5.1": [
{
"has_sig": false,
"upload_time": "2011-07-24T05:01:45",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.5.1.tar.gz",
"md5_digest": "33a6e65d6a4e5b2d91df76256f607b81",
"downloads": 7189,
"filename": "requests-0.5.1.tar.gz",
"packagetype": "sdist",
"size": 23080
}
],
"0.9.0": [
{
"has_sig": false,
"upload_time": "2011-12-28T10:51:35",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.9.0.tar.gz",
"md5_digest": "5f6f03ec76f68a7a3f35120ab5a6c589",
"downloads": 16697,
"filename": "requests-0.9.0.tar.gz",
"packagetype": "sdist",
"size": 55217
}
],
"0.9.1": [
{
"has_sig": false,
"upload_time": "2012-01-06T07:11:02",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.9.1.tar.gz",
"md5_digest": "8ed4667edb5d57945b74a9137adbb8bd",
"downloads": 14372,
"filename": "requests-0.9.1.tar.gz",
"packagetype": "sdist",
"size": 55547
}
],
"0.9.2": [
{
"has_sig": false,
"upload_time": "2012-01-19T03:39:58",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.9.2.tar.gz",
"md5_digest": "65b36d99a4d2f78a22f08c95d2475e33",
"downloads": 4700,
"filename": "requests-0.9.2.tar.gz",
"packagetype": "sdist",
"size": 60967
}
],
"0.9.3": [
{
"has_sig": false,
"upload_time": "2012-01-19T16:51:33",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-0.9.3.tar.gz",
"md5_digest": "b13b6fbfa8fc3fc3c25bae300748053f",
"downloads": 11556,
"filename": "requests-0.9.3.tar.gz",
"packagetype": "sdist",
"size": 61006
}
],
"2.3.0": [
{
"has_sig": false,
"upload_time": "2014-05-16T17:57:05",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.3.0-py2.py3-none-any.whl",
"md5_digest": "f2d850fd48fc10a93aa03d69b87b96b4",
"downloads": 4032003,
"filename": "requests-2.3.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 452902
},
{
"has_sig": false,
"upload_time": "2014-05-16T17:57:02",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.3.0.tar.gz",
"md5_digest": "7449ffdc8ec9ac37bbcd286003c80f00",
"downloads": 4723580,
"filename": "requests-2.3.0.tar.gz",
"packagetype": "sdist",
"size": 429521
}
],
"1.2.2": [
{
"has_sig": false,
"upload_time": "2013-05-21T21:44:44",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.2.2.tar.gz",
"md5_digest": "1f655ab7f2aa7447a1657ed69786f436",
"downloads": 219258,
"filename": "requests-1.2.2.tar.gz",
"packagetype": "sdist",
"size": 348851
}
],
"1.2.3": [
{
"has_sig": false,
"upload_time": "2013-05-25T16:48:36",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.2.3.tar.gz",
"md5_digest": "adbd3f18445f7fe5e77f65c502e264fb",
"downloads": 3278337,
"filename": "requests-1.2.3.tar.gz",
"packagetype": "sdist",
"size": 348854
}
],
"1.2.0": [
{
"has_sig": false,
"upload_time": "2013-03-31T05:28:47",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.2.0.tar.gz",
"md5_digest": "22af2682233770e5468a986f451c51c0",
"downloads": 896245,
"filename": "requests-1.2.0.tar.gz",
"packagetype": "sdist",
"size": 341511
}
],
"1.2.1": [
{
"has_sig": false,
"upload_time": "2013-05-20T20:11:09",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-1.2.1.tar.gz",
"md5_digest": "4d019670b94b17e329007d64e67e045e",
"downloads": 8076,
"filename": "requests-1.2.1.tar.gz",
"packagetype": "sdist",
"size": 348710
}
],
"2.5.2": [
{
"has_sig": false,
"upload_time": "2015-02-23T22:37:39",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.5.2-py2.py3-none-any.whl",
"md5_digest": "7e72dfe8ed9d4ce5fd9dd9d799b3add1",
"downloads": 162592,
"filename": "requests-2.5.2-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 474275
},
{
"has_sig": false,
"upload_time": "2015-02-23T22:37:46",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.5.2.tar.gz",
"md5_digest": "424e2469202c9bace4e8bf4642d4217a",
"downloads": 53980,
"filename": "requests-2.5.2.tar.gz",
"packagetype": "sdist",
"size": 455688
}
],
"2.5.3": [
{
"has_sig": false,
"upload_time": "2015-02-24T16:33:49",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.5.3-py2.py3-none-any.whl",
"md5_digest": "233249f4627ac5481c948e494d2a090e",
"downloads": 873743,
"filename": "requests-2.5.3-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 468593
},
{
"has_sig": false,
"upload_time": "2015-02-24T16:33:58",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.5.3.tar.gz",
"md5_digest": "23bf4fcc89ea8d353eb5353bb4a475b1",
"downloads": 874483,
"filename": "requests-2.5.3.tar.gz",
"packagetype": "sdist",
"size": 448318
}
],
"2.5.0": [
{
"has_sig": false,
"upload_time": "2014-12-01T23:27:51",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.5.0-py2.py3-none-any.whl",
"md5_digest": "9d29a8a0210c236d9329bed49277b3fa",
"downloads": 877099,
"filename": "requests-2.5.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 464196
},
{
"has_sig": false,
"upload_time": "2014-12-01T23:27:58",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.5.0.tar.gz",
"md5_digest": "b8bf3ddca75e7ecf1b6776da1e6e3385",
"downloads": 1056687,
"filename": "requests-2.5.0.tar.gz",
"packagetype": "sdist",
"size": 443222
}
],
"2.5.1": [
{
"has_sig": false,
"upload_time": "2014-12-23T17:55:59",
"comment_text": "",
"python_version": "py2.py3",
"url": "https://pypi.python.org/packages/py2.py3/r/requests/requests-2.5.1-py2.py3-none-any.whl",
"md5_digest": "11dc91bc96c5c5e0b566ce8f9c9644ab",
"downloads": 1893375,
"filename": "requests-2.5.1-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 464421
},
{
"has_sig": false,
"upload_time": "2014-12-23T17:56:08",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.5.1.tar.gz",
"md5_digest": "c270eb5551a02e8ab7a4cbb83e22af2e",
"downloads": 2583555,
"filename": "requests-2.5.1.tar.gz",
"packagetype": "sdist",
"size": 443633
}
],
"2.7.0": [
{
"has_sig": false,
"upload_time": "2015-05-03T15:01:28",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.7.0-py2.py3-none-any.whl",
"md5_digest": "564fb256f865a79f977e57b79d31659a",
"downloads": 8550789,
"filename": "requests-2.7.0-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 470641
},
{
"has_sig": false,
"upload_time": "2015-05-03T15:01:21",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.7.0.tar.gz",
"md5_digest": "29b173fd5fa572ec0764d1fd7b527260",
"downloads": 7696812,
"filename": "requests-2.7.0.tar.gz",
"packagetype": "sdist",
"size": 451723
}
]
},
"urls": [
{
"has_sig": false,
"upload_time": "2015-10-13T12:56:41",
"comment_text": "",
"python_version": "2.7",
"url": "https://pypi.python.org/packages/2.7/r/requests/requests-2.8.1-py2.py3-none-any.whl",
"md5_digest": "46f1d621daa3ab38958a42f51478b1ee",
"downloads": 3302171,
"filename": "requests-2.8.1-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 497953
},
{
"has_sig": false,
"upload_time": "2015-10-13T12:56:34",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/r/requests/requests-2.8.1.tar.gz",
"md5_digest": "a27ea3d72d7822906ddce5e252d6add9",
"downloads": 2462803,
"filename": "requests-2.8.1.tar.gz",
"packagetype": "sdist",
"size": 480803
}
]
}
fades-master/tests/examples/pypi_get_version_fail.json 0000664 0001750 0001750 00000000037 15215454131 022667 0 ustar tchet tchet {"MALFORMED": {"json": "1.0"}}
fades-master/tests/conftest.py 0000664 0001750 0001750 00000003513 15215454131 015777 0 ustar tchet tchet # Copyright 2019-2026 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
import json
import uuid
from pytest import fixture
@fixture(scope="function")
def tmp_file(tmp_path):
"""Fixture for a unique tmpfile for each test."""
yield tmp_path / "testfile"
@fixture(scope="function")
def create_tmpfile(tmp_path):
def add_content(lines):
"""Fixture for a unique tmpfile for each test."""
namefile = tmp_path / f"testfile_{uuid.uuid4()}"
with open(namefile, "w", encoding="utf-8") as f:
for line in lines:
f.write(line + "\n")
return namefile
yield add_content
def pytest_addoption(parser):
"""Define new pytest command line argument to be used by integration tests."""
parser.addoption("--integtest-pyversion", action="store")
@fixture
def fake_venv():
"""Provides a fake venv data."""
def gen(extra="extra metadata", **kwargs):
base = {
"metadata": {"env_path": "foo", "env_bin_path": "bar", "extra": extra},
"installed": {},
"interpreter": "pythonX.Y",
"options": {"foo": "bar"},
}
base.update(kwargs)
return json.dumps(base)
return gen
fades-master/tests/__init__.py 0000664 0001750 0001750 00000003721 15215454131 015712 0 ustar tchet tchet # Copyright 2017-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Common code for the tests."""
import os
from tempfile import mkstemp
from packaging.requirements import Requirement
def get_tempfile(testcase):
"""Return the name of a temp file that will be removed when the test finishes."""
# create the file and close its descriptor
descriptor, tempfile = mkstemp(prefix="test-temp-file")
os.close(descriptor)
def clean():
"""Clean the file from disk, if still there."""
if os.path.exists(tempfile):
os.remove(tempfile)
testcase.addCleanup(clean)
return tempfile
def create_tempfile(testcase, lines):
tempfile = get_tempfile(testcase)
with open(tempfile, 'w', encoding='utf-8') as f:
for line in lines:
f.write(line + '\n')
return tempfile
def get_python_filepaths(roots):
"""Helper to retrieve paths of Python files."""
python_paths = []
for root in roots:
for dirpath, dirnames, filenames in os.walk(root):
for filename in filenames:
if filename.endswith(".py"):
python_paths.append(os.path.join(dirpath, filename))
return python_paths
def get_reqs(*items):
"""Transform text requirements into Requirement objects."""
return [Requirement(item) for item in items]
fades-master/testdev.bat 0000664 0001750 0001750 00000000327 15215454131 014604 0 ustar tchet tchet @echo off
rem Copyright 2018 Facundo Batista, Nicolás Demarchi
if not [%*] == [] (
set TARGET_TESTS="%*"
) else (
set TARGET_TESTS=fades tests
)
bin\fades -r requirements.txt -x pytest -v %TARGET_TESTS%
fades-master/testdev 0000755 0001750 0001750 00000000325 15215454131 014036 0 ustar tchet tchet #!/bin/bash
#
# Copyright 2014-2016 Facundo Batista, Nicolás Demarchi
set -eu
if [ $# -ne 0 ]; then
TARGET_TESTS="$@"
else
TARGET_TESTS=""
fi
./bin/fades -r requirements.txt -x pytest -s $TARGET_TESTS
fades-master/test 0000755 0001750 0001750 00000000646 15215454131 013345 0 ustar tchet tchet #!/bin/bash
#
# Copyright 2014-2018 Facundo Batista, Nicolás Demarchi
set -eu
if [ $# -ne 0 ]; then
TARGET_TESTS="$@"
else
TARGET_TESTS=""
fi
./bin/fades -r requirements.txt -d pytest-cov -x pytest --cov=fades $TARGET_TESTS
# check if we are using exit() in the code.
if grep -r -n ' exit(' --include="*.py" .; then echo 'Please use sys.exit() instead of exit(). https://github.com/PyAr/fades/issues/280'; fi
fades-master/setup.py 0000755 0001750 0001750 00000012355 15215454131 014155 0 ustar tchet tchet #!/usr/bin/env python3
# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check https://github.com/PyAr/fades
"""Build tar.gz for fades.
Needed packages to run (using Debian/Ubuntu package names):
python3
python3-xdg (optional)
"""
import os
import re
import shutil
import sys
from distutils.core import setup
from setuptools.command.install import install
def get_version():
"""Retrieves package version from the file."""
with open('fades/_version.py') as fh:
m = re.search(r"\(([^']*)\)", fh.read())
if m is None:
raise ValueError("Unrecognized version in 'fades/_version.py'")
return m.groups()[0].replace(', ', '.')
# the different scripts according to the platform
SCRIPT_WIN = 'bin/fades.cmd'
SCRIPT_REST = 'bin/fades'
class CustomInstall(install):
"""Custom installation to fix script info and install man."""
def initialize_options(self):
"""Run parent initialization and then fix the scripts var."""
install.initialize_options(self)
# leave the proper script according to the platform
script = SCRIPT_WIN if sys.platform == "win32" else SCRIPT_REST
self.distribution.scripts = [script]
def run(self):
"""Run parent install, and then save the man file."""
install.run(self)
# man directory
if self._custom_man_dir is not None:
if not os.path.exists(self._custom_man_dir):
os.makedirs(self._custom_man_dir)
shutil.copy("man/fades.1", self._custom_man_dir)
def finalize_options(self):
"""Alter the installation path."""
install.finalize_options(self)
if self.prefix is None:
# no place for man page (like in a 'snap')
man_dir = None
else:
man_dir = os.path.join(self.prefix, "share", "man", "man1")
# if we have 'root', put the building path also under it (used normally
# by pbuilder)
if self.root is not None:
man_dir = os.path.join(self.root, man_dir[1:])
self._custom_man_dir = man_dir
setup(
name='fades',
version=get_version(),
license='GPL-3',
author='Facundo Batista, Nicolás Demarchi',
author_email='facundo@taniquetil.com.ar, mail@gilgamezh.me',
description=(
'A system that automatically handles the virtualenvs in the cases '
'normally found when writing scripts and simple programs, and '
'even helps to administer big projects.'),
long_description=open('README.rst').read(),
url='https://github.com/PyAr/fades',
download_url="https://github.com/PyAr/fades/releases", # Release download URL.
packages=["fades"],
scripts=[SCRIPT_WIN, SCRIPT_REST],
keywords="virtualenv utils utility scripts", # to get found easily on PyPI results, etc.
cmdclass={
'install': CustomInstall,
},
install_requires=['setuptools'],
tests_require=['logassert', 'pyxdg', 'pyuca', 'pytest', 'flake8',
'pep257', 'rst2html5'], # what unittests require
python_requires='>=3.10', # Minimum Python version supported.
extras_require={
'pyxdg': 'pyxdg',
'packaging': 'packaging',
},
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
'Development Status :: 5 - Production/Stable',
# 'Development Status :: 6 - Mature',
# 'Development Status :: 7 - Inactive',
'Environment :: Console',
'Intended Audience :: Developers',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: GNU General Public License (GPL)',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Natural Language :: English',
'Natural Language :: Spanish',
'Operating System :: MacOS',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: Implementation',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: Software Development',
'Topic :: Utilities',
],
)
fades-master/resources/ 0000775 0001750 0001750 00000000000 15215454131 014446 5 ustar tchet tchet fades-master/resources/video/ 0000775 0001750 0001750 00000000000 15215454131 015554 5 ustar tchet tchet fades-master/resources/video/slide_monkey.svg 0000664 0001750 0001750 00002257576 15215454131 021007 0 ustar tchet tchet
fades-master/resources/video/slide_intro.svg 0000664 0001750 0001750 00000054566 15215454131 020630 0 ustar tchet tchet
fades-master/resources/video/slide_easy.svg 0000664 0001750 0001750 00000323603 15215454131 020425 0 ustar tchet tchet
fades-master/resources/video/slide_closing.svg 0000664 0001750 0001750 00000242055 15215454131 021123 0 ustar tchet tchet
fades-master/resources/video/slide_awesome.svg 0000664 0001750 0001750 00000011146 15215454131 021120 0 ustar tchet tchet
fades-master/resources/video/script.ods 0000664 0001750 0001750 00000033550 15215454131 017575 0 ustar tchet tchet PK zžH…l9Š. . mimetypeapplication/vnd.oasis.opendocument.spreadsheetPK zžHEh›i@ @ Thumbnails/thumbnail.png‰PNG
IHDR l ÿ #JÃ PLTE
"(&!6((&%%)46(!3-;>2'?;=0D*8I>EN;@P;M`=WiD0A4-H=7IA:VC/ZG5_P?`L;fP>LGHDDRCMXMTUUNGQMRXQLRQQSTZVY_[TQ\QY_^UZY[HViL_sW\bU]mY\dX_mU]p__qZckW`rVdz[fsiT@lXFl^HcXZs\G`_dadXnb]tfTtb]zb\jSzl]dcdbeiehjlkkdjuiszvmg{rissssuysx}ytq|yv{{{Xm‚cu„dw�f}Œit‡kx„lzŽk~‘t{…p–w€…t��}ƒŒ~ˆ�tƒ–x‰ ‘¥Œr_€nl‡wj�vpƒ|u�|xˆ~r‚}ƒƒ€|Žƒv‹‚{”€k“‚r—…yš„s˜‡xž‰wœŽ~ Šw¤“}†‡‰†�•„�ž…‘œŒ�”Œ–�’Š‚•�Ž˜“Œ—˜˜ƒ’£�—ª‡œ§‚˜¬‹–¡�š¤ˆ˜«‰�´–œ¤Œ¡¸–¡š¤ª”¤³”§½–©¿š¤³œ«µœ¬»¦’ƒ¦˜Œª”�¬˜„¯ŸŠ£œ–³�‰¨¢›±¡‘²¥š»¤’¼«š¨§¨§´¨±»¸¢¸²«¸º·™¬Ãž°Å¤³Ä¤¹Ì·Á«¹Ä©¾Ò±ºÃ¬ÀгÀ̽Á½Ä̸ÉÖ¾ÑÞ½Íá¾ÓæÃ«—ì™Ä²žÂ²¡Ä·¨Ã¸Ì¶¢Ìº¤Êº©Å¼³Ñº¥ÏÀ¯ËúÕÁÖȹåÊ·á̺âÒ¾ÄÄÅÅÊÏÏÇÀÊÈÄÈÊÊÄËÓÇÒÜÓÍÆÒÔËßÔÆÛÓÊÒÒÒÒÖÛÕÙÞÜ×ÑßÙÒÚÛÛÄÜïÌÕàÌÙäÌÜéÅÝòÔÛãØÞåÏàîÌáóÚâéÙéôâÔÄáÕÉæÙÇäÙËêÒÀèÜÌäÜÔêâØòêÜåäããæèèåàìéäëìëäíóëò÷óìã÷ðçôñìúóæúôëýøíóóôòöùöûóôùüùöòûùõþþþ’åH“ ûIDATxÚíÛLSW `yé67÷`ÆùÖɪƒ9sÕù˜b-ÔJœ ÚÙh]&
!q–¹0êâ˜6Ÿd¨…Rd>6�AP0®ò)LÄhæPÖ²)m…[/–þ+[XÂm³ýšaý/�äpnÏ×óŸ{zÏï<Ä͇0Â#Œ0Â#Œ0Â#Œ0Âûss%í‚b¡ÒÙ]”
nKW{«Ý+ïäÁîŒ(ñG 4~ë“z°âVª4½7ÂX&oÎFO¹3^éê_áÆi’6Œ¹v¥7°óòö0øN{w¼åÆö|
³Ì¶)–µS�ÿ=v9yBe¤ŠWõÎ_˜a¸&·0B‘.Î{µ0{EEÔ0�&¸âþq«ŒpùØ+šg²±ÿXü»ì,˺X�W³Þ[Ýû®—æ5‡÷(êYo
dº±ÑÊ¡ÊÑ
=«÷GHô^?2úþrgµJÆ*ÕÕZñN«ÌµÖE¾2Ù½ÕY²sQI×tüu$…9'ε¸Ú²@Ö:}k76æš×sš.š¯ózðž”(·õÍ
N÷[¬
ªøfÚ¨ _]ë—3DþÜçCò¡ÝßsJ7"¬¥eÝfoØô à§lýœ+b~þêY˜¾éŒÏa ‡ÿ�n¬að�ƾ3„Gà¶?S< �@0s!´ý02ðÀ¾ñÝX8(Ì_ïÙ0Ò[ïøê/ì“òÄN¿†ÆÀãóª�ÜöUõ»çgR�ûâ²Èj¸ëß ÈxsIàž6(í™5és_ø G£5 Tç«Ë
µ*£»¬5¹ƒž£�,&ÏTSéÈ:äÞí�í֬Ƣª†s*³M¥a^ÊÐCß1Àù/{Æ÷Ü£ióø|‚dI�S8j$Û0Í\��#au++‹máÆ�´xÉ$êëUˆSqÂ�ÅöåÃlî;»Ù…%oÅt�+ô�ã±Ø©í0›ñÄRQ>³'¤MÄb¶0Ý$Níû¦Ö¥å›a¬ZY¼mÍôbÑp;fÑhŽÙ5×ýj×hrؼc´"~ȘAeâ»FŨ?V^E5Tªº†Çš¦^ãÔeï(Œk6)N¢ÖÆ1Í#ð˜·I
g>�ß–áμ„XjÎqk(ûU,•qÆÌ%ÞqAš‹9ÎÔ FGªÒDóŒ0Â#Œ0Â#Œ0Â#Œ0Â#Œ0Â#Œ0Â#Œ0Â#Œ0Â#Œ°Gûv�‰ÿàM IEND®B`‚PK zžH settings.xmlíZÝs¢:¿E‡×;]Ñî:ÅiÝAÖ¯ÝÖmA°õ-@Ô´!a’ Ú¿~êÞ.+«EØÙ;÷ú J’ß999ßpõqà³%dQrÔß©Ê$õ™_+ö¸w~©|lÿuEg3äÁ–O½(€Dœs(„œÂÏärÂ[›ák%b¤EG¼E@ yKx-B²[Öz=»•ÛÜYaDž¯•…a«V‹ãø]|ñ޲y®iZ-ÝMõ(™¡ù±¤6³_“¢”~'”,Ø0“k¨êûÚæ¿r¶eò•hJ{'‡ÝöÛW[›Ë90Hds¶½�°vH’%‚ñw©)ûÖý¸ÆA¹ê‚1
•Ý X‡r¡´Õ«ÚÏ o¾�3Q
òùb±úBÓ.'à š/ö²Þ¼h^Ö�Å?@xŽˆWÐÏÒ‚ñþSJ×Hýbëc8†ñÐϰɓ* ´…x§ h†Ï1�9Äè�Kô Ô�P@#bœ²;Ê‘�úÿ°÷0‹ä�È�û�/‹!(C/”€#qK}˜•þ‚²42�¼ªÐ3ÜïT¦…¾æ¿|Ýh St�yŽx
ÚÿŽß’½ÖÖÌs)'â–ë½w¨*
JžRŒ%J©�€: GYÔ”Q¨À&žõ—àÍ‚àÖ‚Æ}†²NÛ¥C@”¶`Ü�¼çæëX‘7œ:õc-+u÷9ñ$õë';�QŒ]Àrxý¢ñ¿†�ªa �ŽÌcžïLò€