diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml new file mode 100644 index 0000000..cc7d2b4 --- /dev/null +++ b/.github/workflows/pythonpackage.yml @@ -0,0 +1,29 @@ +name: build + +on: [push] + +jobs: + tester: + name: Test them all + runs-on: ${{ matrix.operating-system }} + strategy: + max-parallel: 4 + matrix: + operating-system: [ubuntu-latest] + python-version: [3.8, 3.11] + + steps: + - uses: actions/checkout@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest>=3.6 mock "pytest-cov<2.6.0" codecov win_inet_pton + pip install -r requirements.txt + - name: Test with pytest + run: | + pip install pytest + pytest diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 53b3c59..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: python - -matrix: - fast_finish: true - include: - - python: 3.6 - - python: 3.7 - - os: osx - osx_image: xcode11 - language: shell - -install: - - python setup.py install - - pip install pytest>=3.6 mock "pytest-cov<2.6.0" codecov -script: - - py.test --cov=socks5man -after_success: - - codecov diff --git a/README.md b/README.md new file mode 100644 index 0000000..d022e86 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +Socks5man is a Socks5 management tool and Python library. It +enables you to add socks5 servers, run a service that verifies if +they are operational, and request these servers in a round-robin fashion +by country, city, average connection time, and bandwidth, using the Python library. + +The library also allows for manual operationality, bandwidth, and connection time tests. +A local database is used to lookup country and city information for a host ip. + +The documentation can be found at: [https://socks5man.readthedocs.io](https://socks5man.readthedocs.io). + +This product includes GeoLite2 data created by MaxMind, available from [maxmind.com](maxmind.com). + +![](https://github.com/RicoVZ/socks5man/workflows/build/badge.svg) + +* [maxmind.com](http://www.maxmind.com) +* [https://socks5man.readthedocs.io](https://socks5man.readthedocs.io) diff --git a/README.rst b/README.rst deleted file mode 100644 index 6c5b8e5..0000000 --- a/README.rst +++ /dev/null @@ -1,26 +0,0 @@ -Socks5man is a Socks5 management tool and Python library. It -enables you to add socks5 servers, run a service that verifies if -they are operational, and request these servers in a round-robin fashion -by country, city, average connection time, and bandwidth, using the Python library. - -The library also allows for manual operationality, bandwidth, and connection time tests. -A local database is used to lookup country and city information for a host ip. - -The documentation can be found at: `https://socks5man.readthedocs.io`_. - -This product includes GeoLite2 data created by MaxMind, available from `maxmind.com`_. - -.. image:: https://api.travis-ci.org/RicoVZ/socks5man.svg?branch=master - :alt: Linux and OSX Build Status - :target: https://travis-ci.org/RicoVZ/socks5man - -.. image:: https://ci.appveyor.com/api/projects/status/le7o92n6t1glv4su?svg=true - :alt: Windows Build Status - :target: https://ci.appveyor.com/project/RicoVZ/socks5man - -.. image:: https://codecov.io/gh/ricovz/socks5man/branch/master/graph/badge.svg - :alt: Codecov Coverage Status - :target: https://codecov.io/gh/RicoVZ/socks5man - -.. _`maxmind.com`: http://www.maxmind.com -.. _`https://socks5man.readthedocs.io`: https://socks5man.readthedocs.io/ diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index d65067f..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,20 +0,0 @@ -environment: - matrix: - - PYTHON: "C:\\Python36" - PYTHON_VERSION: 3.6 - - - PYTHON: "C:\\Python37" - PYTHON_VERSION: 3.7 - -install: - - "python.exe setup.py install" - - "pip.exe install -e ." - - "pip.exe install pytest mock pytest-cov codecov" - -build: off - -test_script: - - "pytest.exe --cov=socks5man" - -after_test: - - "codecov.exe" diff --git a/docs/source/conf.py b/docs/source/conf.py index bb1d6a2..4c9b6ab 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -19,6 +19,7 @@ # -- Project information ----------------------------------------------------- +from __future__ import absolute_import project = u'Socks5man' copyright = u'2016-2018' author = u'Ricardo van Zutphen' diff --git a/requirements.txt b/requirements.txt index a982365..4286556 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -PySocks==1.5.7 -geoip2==2.9.0 -SQLAlchemy>=1.3.3, <1.4 -click==6.6 -alembic>=1.0.7, <1.1 +PySocks>=1.7 +geoip2>=2.9.0 +SQLAlchemy +click +alembic diff --git a/setup.py b/setup.py index 328f0c5..c5a8227 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,10 @@ -import sys +from __future__ import absolute_import from setuptools import setup -if sys.version[0] != "2": - sys.exit( - "Socks5man currently only supports Python 2.7. 3.5+ is on the roadmap" - ", but is not supported yet. For now, please install it in the" - " following way: `pip2 install -U socks5man`." - ) - setup( name="Socks5man", - version="0.2.0", + version="0.3.0", author="Ricardo van Zutphen", author_email="ricardo@hatching.io", packages=[ @@ -37,11 +30,11 @@ ], license="GPLv3", description="SOCKS5 server management tool and library", - long_description=open("README.rst", "rb").read(), + long_description=open("README.md", "r").read(), include_package_data=True, url="https://github.com/RicoVZ/socks5man", install_requires=[r.strip() for r in open("requirements.txt", "r").readlines()], - python_requires=">=2.7, <3.0", + python_requires=">=3.6", extras_require={ ":sys_platform == 'win32'": [ "win-inet-pton==1.0.1", diff --git a/socks5man/__init__.py b/socks5man/__init__.py index e69de29..56f3b36 100644 --- a/socks5man/__init__.py +++ b/socks5man/__init__.py @@ -0,0 +1 @@ + diff --git a/socks5man/config.py b/socks5man/config.py index 2664afc..c0a2d9c 100644 --- a/socks5man/config.py +++ b/socks5man/config.py @@ -1,5 +1,6 @@ -import ConfigParser +from __future__ import absolute_import import os +import configparser from socks5man.exceptions import Socks5ConfigError from socks5man.misc import cwd @@ -43,7 +44,7 @@ def read(self): if Config._cache: Config._cache = {} - config = ConfigParser.ConfigParser() + config = configparser.ConfigParser() confpath = cwd("conf", "socks5man.conf") if not os.path.isfile(confpath): @@ -53,7 +54,7 @@ def read(self): ) try: config.read(confpath) - except ConfigParser.Error as e: + except configparser.Error as e: raise Socks5ConfigError( "Cannot parse config file. Error: %s" % e ) diff --git a/socks5man/database.py b/socks5man/database.py index d4df08a..afad8ca 100644 --- a/socks5man/database.py +++ b/socks5man/database.py @@ -1,10 +1,11 @@ +from __future__ import absolute_import import logging import os from datetime import datetime from sqlalchemy import ( Column, Integer, String, DateTime, Boolean, Text, create_engine, - Float, and_, func + Float, and_, func, inspect ) from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.ext.declarative import declarative_base @@ -45,12 +46,14 @@ class Socks5(Base): connect_time = Column(Float(), nullable=True) description = Column(Text(), nullable=True) dnsport = Column(Integer(), nullable=True) + private = Column(Boolean, nullable=True) - def __init__(self, host, port, country, country_code): + def __init__(self, host, port, country, country_code, private): self.host = host self.port = port self.country = country self.country_code = country_code + self.private = private def to_dict(self): """Converts object to dict. @@ -62,7 +65,7 @@ def to_dict(self): value = getattr(self, column.name) if isinstance(value, datetime): socks_dict[column.name] = value.strftime("%Y-%m-%d %H:%M:%S") - elif isinstance(value, (str, basestring)): + elif isinstance(value, str): socks_dict[column.name] = value.encode("utf-8") else: socks_dict[column.name] = value @@ -77,9 +80,7 @@ def __repr__(self): ) -class Database(object): - - __metaclass__ = Singleton +class Database(object, metaclass=Singleton): def __init__(self): self.connect(create=True) @@ -90,9 +91,7 @@ def connect(self, create=False): if create: if not os.path.exists(cwd("socks5man.db")): self._create() - elif not self.engine.dialect.has_table( - self.engine, AlembicVersion.__tablename__ - ): + elif not inspect(self.engine).has_table(AlembicVersion.__tablename__): AlembicVersion.__table__.create(self.engine) def _create(self): @@ -117,9 +116,9 @@ def db_migratable(self): ses.close() def add_socks5(self, host, port, country, country_code, operational=False, - city=None, username=None, password=None, dnsport=None, description=None): + city=None, username=None, password=None, dnsport=None, description=None, private=False): """Add new socks5 server to the database""" - socks5 = Socks5(host, port, country, country_code) + socks5 = Socks5(host, port, country, country_code, private) socks5.operational = operational socks5.city = city socks5.username = username @@ -381,7 +380,7 @@ def bulk_delete_socks5(self, ids_list): @param ids_list: A list of socks5 ids to delete""" chunk = 100 try: - for c in xrange(0, len(ids_list), chunk): + for c in range(0, len(ids_list), chunk): self.engine.execute( Socks5.__table__.delete().where( Socks5.id.in_(ids_list[c:c+chunk]) diff --git a/socks5man/helpers.py b/socks5man/helpers.py index d7477c4..5ae560e 100644 --- a/socks5man/helpers.py +++ b/socks5man/helpers.py @@ -1,9 +1,11 @@ +from __future__ import absolute_import import logging import socket import socks import struct import time -import urllib2 +import urllib.request +import urllib.error from socks5man.config import cfg from socks5man.constants import IANA_RESERVERD_IPV4_RANGES @@ -48,6 +50,8 @@ def is_ipv4(ip): """Try to parse string as Ipv4. Return True if success, False otherwise""" try: + if not isinstance(ip, type(str)): + ip = str(ip) socket.inet_aton(ip) return True except socket.error: @@ -125,19 +129,18 @@ def validify_host_port(host, port): def get_over_socks5(url, host, port, username=None, password=None, timeout=3): """Make a HTTP GET request over socks5 of the given URL""" - socks.set_default_proxy( - socks.SOCKS5, host, port, - username=username, password=password - ) + + socks.set_default_proxy(socks.SOCKS5, host, port, username=username, password=password) response = None + clean_socket = socket.socket try: socket.socket = socks.socksocket - response = urllib2.urlopen(url, timeout=timeout).read() - except (socket.error, urllib2.URLError, socks.ProxyError) as e: + response = urllib.request.urlopen(url, timeout=timeout).read() + except urllib.error.URLError as e: log.error("Error making HTTP GET over socks5: %s", e) finally: - socket.socket = socket._socketobject + socket.socket = clean_socket return response def approximate_bandwidth(host, port, username=None, password=None, diff --git a/socks5man/logs.py b/socks5man/logs.py index 9772201..e8e4d62 100644 --- a/socks5man/logs.py +++ b/socks5man/logs.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import copy import logging import sys diff --git a/socks5man/main.py b/socks5man/main.py index 8549933..7a7667b 100644 --- a/socks5man/main.py +++ b/socks5man/main.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import +from __future__ import print_function import click import csv import logging @@ -63,7 +65,8 @@ def verify(repeated, operational, non_operational, unverified): @click.option("-u", "--username", help="Username for this socks5 server") @click.option("-p", "--password", help="Password for this socks5 server") @click.option("-d", "--description", help="Description for this socks5 server") -def add(host, port, username, password, description): +@click.option("-pi", "--private", is_flag=True, help="Private server ip") +def add(host, port, username, password, description, private): """Add socks5 server.""" if username and not password or password and not username: log.warning( @@ -75,7 +78,7 @@ def add(host, port, username, password, description): try: entry = m.add( host, port, username=username, password=password, - description=unicode(description) + description=description, private=private ) except Socks5manError as e: log.error("Failed to add socks5 server: %s", e) @@ -217,20 +220,22 @@ def list(country, code, city, host, operational, non_operational, count, sys.exit(0) if not export: - print( + print(( "{:<4} {:<12} {:<20} {:<5} {:<16} {:<12} {:<16} {:<16} {:<16}{:<16}".format( "ID", "Operational", "Host", "Port", "Country", "Country Code", "City", "Username", "Password", "Description", ) - ) + )) for socks5 in socks5s: print( "{:<4} {:<12} {:<20} {:<5} {:<16} {:<12} {:<16} {:<16} {:<16} {:<16}".format( socks5.id, "Yes" if socks5.operational else "No", socks5.host, socks5.port, socks5.country, socks5.country_code, socks5.city, - socks5.username, socks5.password, socks5.description + socks5.username if socks5.username else "", socks5.password if socks5.password else "", + socks5.description if socks5.description else "" ) ) + sys.exit(0) if os.path.exists(export): @@ -243,10 +248,10 @@ def list(country, code, city, host, operational, non_operational, count, for socks5 in socks5s: socks5_d = socks5.to_dict() if header: - csv_w.writerow(socks5_d.keys()) + csv_w.writerow(list(socks5_d.keys())) header = False - csv_w.writerow(socks5_d.values()) + csv_w.writerow(list(socks5_d.values())) @main.command() diff --git a/socks5man/manager.py b/socks5man/manager.py index 50c921a..d47b52e 100644 --- a/socks5man/manager.py +++ b/socks5man/manager.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import logging from socks5man.database import Database @@ -53,7 +54,7 @@ def acquire(self, country=None, country_code=None, city=None, return None def add(self, host, port, username=None, password=None, dnsport=None, - description=None): + description=None, private=False): """Add a socks5 server. :param host: IP or a valid hostname of the socks5 server. @@ -66,6 +67,8 @@ def add(self, host, port, username=None, password=None, dnsport=None, (optional) :param description: Description to store with the socks5 server (optional) + :param private: IP type, private server + (optional) :return: A dictionary containing the provided information, the generated id, the determined country, country code, and city. :rtype: dict @@ -121,13 +124,13 @@ def add(self, host, port, username=None, password=None, dnsport=None, host=host, port=port, username=username, - password=password + password=password, ) entry.update(GeoInfo.ipv4info(valid_entry.ip)) socksid = db.add_socks5( entry.host, entry.port, entry.country, entry.country_code, city=entry.city, username=entry.username, password=entry.password, - dnsport=dnsport, description=description, + dnsport=dnsport, description=description, private=private, ) entry["id"] = socksid @@ -190,7 +193,8 @@ def bulk_add(self, socks5_dict_list, description=None): "password": password, "operational": False, "dnsport": entry.get("dnsport"), - "description": entry.get("description") + "description": entry.get("description"), + "private": entry.get("private"), } new_entry.update(GeoInfo.ipv4info(valid_entry.ip)) new.append(new_entry) diff --git a/socks5man/misc.py b/socks5man/misc.py index dd6f91e..4fa24b6 100644 --- a/socks5man/misc.py +++ b/socks5man/misc.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import hashlib import os import shutil @@ -48,7 +49,7 @@ def unpack_mmdb(tarpath, to): break geodb_hash = md5(tarpath) - with open(cwd("geodb", ".version"), "wb") as fw: + with open(cwd("geodb", ".version"), "w") as fw: fw.write(geodb_hash) def set_cwd(path): diff --git a/socks5man/setupdata/db_migration/env.py b/socks5man/setupdata/db_migration/env.py index 0294697..dc6de6f 100644 --- a/socks5man/setupdata/db_migration/env.py +++ b/socks5man/setupdata/db_migration/env.py @@ -1,5 +1,6 @@ from __future__ import with_statement +from __future__ import absolute_import from logging.config import fileConfig from alembic import context diff --git a/socks5man/setupdata/db_migration/versions/add_socks_dns_port.py b/socks5man/setupdata/db_migration/versions/add_socks_dns_port.py index 634218d..546f9e9 100644 --- a/socks5man/setupdata/db_migration/versions/add_socks_dns_port.py +++ b/socks5man/setupdata/db_migration/versions/add_socks_dns_port.py @@ -7,6 +7,7 @@ """ # Revision identifiers, used by Alembic. +from __future__ import absolute_import revision = '2910ee00d182' down_revision = '2b221e84eb82' diff --git a/socks5man/socks5.py b/socks5man/socks5.py index fffdaa6..bb679a5 100644 --- a/socks5man/socks5.py +++ b/socks5man/socks5.py @@ -1,5 +1,5 @@ +from __future__ import absolute_import import logging -import socket import socks import sys import time @@ -43,20 +43,23 @@ def verify(self): if not is_ipv4(ip): ip = get_ipv4_hostname(ip) - response = get_over_socks5( - cfg("operationality", "ip_api"), self.host, self.port, - username=self.username, password=self.password, - timeout=cfg("operationality", "timeout") - ) + try: + response = get_over_socks5( + cfg("operationality", "ip_api"), self.host, self.port, + username=self.username, password=self.password, + timeout=cfg("operationality", "timeout") + ) + except AttributeError: + return operational if response: - if ip == response: + if ip == response.decode("utf-8"): operational = True # If a private ip is used, the api response will not match with # the configured host or its ip. There was however a response, # therefore we still mark it as operational - elif is_reserved_ipv4(ip) and is_ipv4(response): + elif self.private or (is_reserved_ipv4(ip) and is_ipv4(response.decode("utf-8"))): operational = True db.set_operational(self.id, operational) @@ -102,7 +105,8 @@ def measure_connection_time(self): cfg("connection_time", "port") )) s.close() - except (socks.ProxyError, socket.error) as e: + # socket.error, socks.ProxyError + except Exception as e: log.error("Error connecting in connection time test: %s", e) connect_time = None else: @@ -137,7 +141,7 @@ def host(self): :rtype: str """ if self.db_socks5.host: - return self.db_socks5.host.encode("utf-8") + return self.db_socks5.host return None @property @@ -157,7 +161,7 @@ def country(self): :rtype: str """ if self.db_socks5.country: - return self.db_socks5.country.encode("utf-8") + return self.db_socks5.country return None @property @@ -178,7 +182,7 @@ def city(self): :rtype: str """ if self.db_socks5.city: - return self.db_socks5.city.encode("utf-8") + return self.db_socks5.city return None @property @@ -189,7 +193,7 @@ def username(self): :rtype: str """ if self.db_socks5.username: - return self.db_socks5.username.encode("utf-8") + return self.db_socks5.username return None @property @@ -266,9 +270,19 @@ def description(self): :rtype: str """ if self.db_socks5.description: - return self.db_socks5.description.encode("utf-8") + return self.db_socks5.description return None + @property + def private(self): + """ + Boolean that tells if the server is private ip. + + :rtype: bool + """ + return self.db_socks5.private + + def __repr__(self): return "" % ( self.host, self.port, self.country, ( diff --git a/socks5man/tools.py b/socks5man/tools.py index 9d1081d..a2eee33 100644 --- a/socks5man/tools.py +++ b/socks5man/tools.py @@ -1,9 +1,10 @@ +from __future__ import absolute_import import logging import os import socket import shutil import time -import urllib2 +import urllib from socks5man.config import cfg from socks5man.database import Database @@ -54,7 +55,6 @@ def verify_all(repeated=False, operational=None, unverified=None): continue if cfg("bandwidth", "enabled"): - print "BLABLA 2" if last_bandwidth: waited = time.time() - last_bandwidth if waited < cfg("socks5man", "bandwidth_interval"): @@ -63,9 +63,9 @@ def verify_all(repeated=False, operational=None, unverified=None): if not download_verified: download_url = cfg("bandwidth", "download_url") try: - urllib2.urlopen(download_url, timeout=5) + urllib.request.urlopen(download_url, timeout=5) download_verified = True - except (socket.error, urllib2.URLError) as e: + except (socket.error, urllib.error.URLError) as e: log.error( "Failed to download speed test file: '%s'. Please" " verify the configured file is still online!" @@ -106,8 +106,8 @@ def update_geodb(): current_version = fp.read() try: - latest_version = urllib2.urlopen(cfg("geodb", "geodb_md5_url")).read() - except urllib2.URLError as e: + latest_version = urllib.request.urlopen(cfg("geodb", "geodb_md5_url")).read() + except urllib.error.URLError as e: log.error("Error retrieving latest geodb version hash: %s", e) return @@ -124,8 +124,8 @@ def update_geodb(): try: url = cfg("geodb", "geodb_url") log.info("Downloading latest version: '%s'", url) - mmdbtar = urllib2.urlopen(url).read() - except urllib2.URLError as e: + mmdbtar = urllib.request.urlopen(url).read() + except urllib.error.URLError as e: log.error( "Failed to download new mmdb tar. Is the URL correct? %s", e ) diff --git a/tests/helpers.py b/tests/helpers.py index 059201b..50e771c 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import os import shutil import tempfile diff --git a/tests/test_config.py b/tests/test_config.py index e8ee22e..81d8b7b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,6 @@ -import copy +from __future__ import absolute_import import os +import copy import pytest from socks5man.config import Config, cfg, confbool @@ -9,6 +10,7 @@ from tests.helpers import CleanedTempFile + class TestConfig(object): def setup_class(self): @@ -31,18 +33,18 @@ def test_cfg_defaults(self): create_cwd(cwd()) assert isinstance(cfg("socks5man", "verify_interval"), int) assert isinstance(cfg("socks5man", "bandwidth_interval"), int) - assert isinstance(cfg("operationality", "ip_api"), (str, basestring)) + assert isinstance(cfg("operationality", "ip_api"), (str)) assert isinstance(cfg("operationality", "timeout"), int) assert isinstance(cfg("connection_time", "enabled"), bool) assert isinstance(cfg("connection_time", "timeout"), int) - assert isinstance(cfg("connection_time", "hostname"),(str, basestring)) + assert isinstance(cfg("connection_time", "hostname"), (str)) assert isinstance(cfg("connection_time", "port"), int) assert isinstance(cfg("bandwidth", "enabled"), bool) - assert isinstance(cfg("bandwidth", "download_url"), (str, basestring)) + assert isinstance(cfg("bandwidth", "download_url"), (str)) assert isinstance(cfg("bandwidth", "times"), int) assert isinstance(cfg("bandwidth", "timeout"), int) - assert isinstance(cfg("geodb", "geodb_url"), (str, basestring)) - assert isinstance(cfg("geodb", "geodb_md5_url"), (str, basestring)) + assert isinstance(cfg("geodb", "geodb_url"), (str)) + assert isinstance(cfg("geodb", "geodb_md5_url"), (str)) def test_cfg_values(self): create_cwd(cwd()) @@ -76,8 +78,8 @@ def test_missing_conf(self): def test_invalid_conf(self): create_cwd(cwd()) Config._cache = {} - with open(cwd("conf", "socks5man.conf"), "wb") as fw: - fw.write(os.urandom(512)) + with open(cwd("conf", "socks5man.conf"), "w") as fw: + fw.write("socks5man to dominate them all") with pytest.raises(Socks5ConfigError): cfg("socks5man", "verify_interval") diff --git a/tests/test_config_values.py b/tests/test_config_values.py index 8062d2e..c5a21a9 100644 --- a/tests/test_config_values.py +++ b/tests/test_config_values.py @@ -1,6 +1,7 @@ +from __future__ import absolute_import import re import socket -import urllib2 +import urllib.request from socks5man.config import cfg from socks5man.misc import set_cwd, create_cwd, cwd @@ -20,12 +21,12 @@ def setup(self): def test_ip_api(self): """Verify that the default ip api returns an actual ip""" create_cwd(cwd()) - res = urllib2.urlopen( + res = urllib.request.urlopen( cfg("operationality", "ip_api"), timeout=cfg("operationality", "timeout") ) assert res.getcode() == 200 - assert re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", res.read()) + assert re.match(rb"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", res.read()) def test_measure_time_host(self): """Verify that the default connection measurement still accepts @@ -42,7 +43,7 @@ def test_download_url(self): """Verify that the url used to measure an approximate bandwidth is still available""" create_cwd(cwd()) - res = urllib2.urlopen( + res = urllib.request.urlopen( cfg("bandwidth", "download_url"), timeout=cfg("bandwidth", "timeout") ) @@ -51,6 +52,6 @@ def test_download_url(self): def test_geoipdb_hash_url(self): create_cwd(cwd()) - res = urllib2.urlopen(cfg("geodb", "geodb_md5_url")) + res = urllib.request.urlopen(cfg("geodb", "geodb_md5_url")) assert res.getcode() == 200 assert len(res.read()) == 32 diff --git a/tests/test_database.py b/tests/test_database.py index a929a0f..9b0729b 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import time import pytest diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d6a384c..30ba0a7 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -1,5 +1,6 @@ +from __future__ import absolute_import import mock -import urllib2 +import urllib.request, urllib.error, urllib.parse from socks5man.helpers import ( Dictionary, is_ipv4, is_reserved_ipv4, GeoInfo, get_ipv4_hostname, @@ -134,7 +135,7 @@ def test_validify_host_port(): assert res11 is None @mock.patch("socks5man.helpers.socket") -@mock.patch("urllib2.urlopen") +@mock.patch("urllib.request.urlopen") @mock.patch("socks5man.helpers.socks") def test_get_over_socks5(ms, mu, mss): mss.socket = "DOGE" @@ -152,16 +153,16 @@ def test_get_over_socks5(ms, mu, mss): ) mu.assert_called_once_with("http://example.com", timeout=10) assert res == "many content, such wow" - assert mss.socket == "socket" + assert mss.socket == "DOGE" @mock.patch("socks5man.helpers.socket") -@mock.patch("urllib2.urlopen") +@mock.patch("urllib.request.urlopen") @mock.patch("socks5man.helpers.socks") def test_get_over_socks5_fail(ms, mu, mss): mss.socket = "DOGE" mss._socketobject = "socket" httpresponse = mock.MagicMock() - httpresponse.read.side_effect = urllib2.URLError("Error") + httpresponse.read.side_effect = urllib.error.URLError("Error") mu.return_value = httpresponse ms.socksocket = "socksocket" res = get_over_socks5( @@ -169,7 +170,7 @@ def test_get_over_socks5_fail(ms, mu, mss): password="doge", timeout=10 ) assert res is None - assert mss.socket == "socket" + assert mss.socket == "DOGE" @mock.patch("time.time") @mock.patch("socks5man.helpers.cfg") @@ -266,3 +267,4 @@ def test_approximate_bandwidth_failed(mg, mc, mt): times=2, maxfail=1 ) assert speed is None + diff --git a/tests/test_logs.py b/tests/test_logs.py index 31d7b6c..994b20d 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import logging import mock import os diff --git a/tests/test_manager.py b/tests/test_manager.py index 4833424..638c6b4 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import datetime import pytest @@ -8,6 +9,7 @@ from socks5man.socks5 import Socks5 from tests.helpers import CleanedTempFile +from six.moves import range class TestManager(object): diff --git a/tests/test_misc.py b/tests/test_misc.py index ad14173..08e958a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import mock import os import tempfile @@ -71,7 +72,7 @@ def teardown_class(self): def test_md5(self): fd, path = self.tempfile.mkstemp() - os.write(fd, "tosti") + os.write(fd, b"tosti") os.close(fd) assert md5(path) == "9e796589d183889f5c65af8b736490bb" @@ -86,7 +87,7 @@ def test_unpack_mmdb(self): assert os.path.isfile(mmdb_p) version_file = os.path.join(tmpdir, "geodb", ".version") assert os.path.isfile(version_file) - assert md5(tar_p) == open(version_file, "rb").read() + assert md5(tar_p) == open(version_file, "r").read() r = geodatabase.Reader(mmdb_p) geodata = r.city("8.8.8.8") assert geodata.country.name.lower() == "united states" diff --git a/tests/test_socks5.py b/tests/test_socks5.py index 15f590c..7d27320 100644 --- a/tests/test_socks5.py +++ b/tests/test_socks5.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import datetime import mock import socket @@ -72,7 +73,7 @@ def test_attrs_invalid(self): @mock.patch("socks5man.socks5.get_over_socks5") def test_verify(self, mg): create_cwd(cwd()) - mg.return_value = "8.8.8.8" + mg.return_value = b"8.8.8.8" self.db.add_socks5( "8.8.8.8", 1337, "germany", "DE", city="Frankfurt", operational=False, username="doge", @@ -106,11 +107,11 @@ def test_verify_fail(self, mg): @mock.patch("socks5man.socks5.get_over_socks5") def test_verify_private(self, mg): create_cwd(cwd()) - mg.return_value = "8.8.8.8" + mg.return_value = b"8.8.8.8" self.db.add_socks5( "192.168.0.50", 1337, "germany", "DE", city="Frankfurt", operational=False, username="doge", - password="wow", description="Such wow, many socks5" + password="wow", description="Such wow, many socks5", ) db_socks5 = self.db.view_socks5(1) s = Socks5(db_socks5) @@ -121,7 +122,7 @@ def test_verify_private(self, mg): @mock.patch("socks5man.socks5.get_over_socks5") def test_verify_hostname(self, mg): create_cwd(cwd()) - mg.return_value = "93.184.216.34" + mg.return_value = b"93.184.216.34" self.db.add_socks5( "example.com", 1337, "germany", "DE", city="Frankfurt", operational=False, username="doge", @@ -218,15 +219,15 @@ def test_socks5_to_dict(self): s = self.db.view_socks5(1) socks5 = Socks5(s) d = socks5.to_dict() - assert d["host"] == "example.com" + assert d["host"] == b"example.com" assert d["port"] == 1337 - assert d["country"] == "germany" - assert d["country_code"] == "DE" - assert d["city"] == "Frankfurt" + assert d["country"] == b"germany" + assert d["country_code"] == b"DE" + assert d["city"] == b"Frankfurt" assert not d["operational"] - assert d["username"] == "doge" - assert d["password"] == "wow" - assert d["description"] == "Such wow, many socks5" + assert d["username"] == b"doge" + assert d["password"] == b"wow" + assert d["description"] == b"Such wow, many socks5" assert d["added_on"] == socks5.added_on.strftime("%Y-%m-%d %H:%M:%S") def test_repr(self): @@ -254,3 +255,4 @@ def test_win_imported_win_inet_pton(self): assert "win_inet_pton" in sys.modules else: assert "win_inet_pton" not in sys.modules + diff --git a/tests/test_tools.py b/tests/test_tools.py index 7cbcdad..d1bbe44 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import mock import socket @@ -82,7 +83,7 @@ def test_conntime_fail(self, ms): socks5.approx_bandwidth.assert_not_called() Config._cache["bandwidth"]["enabled"] = False - @mock.patch("socks5man.tools.urllib2.urlopen") + @mock.patch("socks5man.tools.urllib.request.urlopen") @mock.patch("socks5man.tools.Socks5") def test_download_verify_fail(self, ms, mu): create_cwd(cwd())