Merge branch 'python27-compat' into 'master'

python 2.7 compatibility changes

See merge request laudom/librefi!5
master
Lauren Liberda 2020-11-29 04:05:37 +00:00
commit 01b2aa1e12
30 changed files with 3383 additions and 38 deletions

View File

@ -1,17 +1,42 @@
default:
image: python:3.8-alpine
before_script:
- pip3 install -r librefi/requirements.txt
- pip3 install -r librefi/dev_requirements.txt
- pip install -r requirements.txt
- pip install -r requirements_dev.txt
unit_tests:
py2.7-unit_tests:
image: python:2.7-alpine
script:
- nosetests tests/*.py
- python setup.py sdist bdist_wheel
integration_test:
py3.8-unit_tests:
image: python:3.8-alpine
script:
- nosetests tests/*.py
- python setup.py sdist bdist_wheel
py2.7-integration_test:
image: python:2.7-buster
before_script:
- pip install -r requirements.txt
- apt-get -y update
- apt-get -y install network-manager
script:
- python -m librefi
py3.8-integration_test:
image: python:3.8-buster
before_script:
- pip3 install -r librefi/requirements.txt
- pip install -r requirements.txt
- apt-get -y update
- apt-get -y install network-manager
script:
- python3 -m librefi
py3.9-integration_test:
image: python:3.9-buster
before_script:
- pip install -r requirements.txt
- apt-get -y update
- apt-get -y install network-manager
script:

1
MANIFEST.in Normal file
View File

@ -0,0 +1 @@
include README.md LICENSE

6
librefi/__init__.py Normal file
View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .librefi import LibreFi
__all__ = ['LibreFi']

View File

@ -1,5 +1,18 @@
from .librefi import LibreFi
from .logger import LOG_LEVELS
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
librefi = LibreFi(log_level=LOG_LEVELS.DEBUG)
librefi._periodical_check()
import sys
if __package__ is None and not hasattr(sys, 'frozen'):
# direct call of __main__.py
import os.path
path = os.path.realpath(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(os.path.dirname(path)))
from librefi import LibreFi
from .logger import _Logger, LOG_LEVELS
librefi = LibreFi(logger=_Logger, log_level=LOG_LEVELS.DEBUG)
if __name__ == '__main__':
librefi._periodical_check()

3069
librefi/compat.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,12 @@
import sys
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ..compat import compat_sys_platform
from ..utils import LFiError
def get_connector():
if sys.platform == "linux":
if compat_sys_platform == "linux":
from .networkmanager import NetworkManagerConnector
return NetworkManagerConnector
raise LFiError('Could not find a connector')

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import subprocess
import pipes
import re
@ -7,20 +10,22 @@ class NetworkManagerConnector:
NMCLI_BASE = ["nmcli", "--mode", "tabular", "--terse", "--colors", "no"]
def _call_nmcli(self, args, parse=True):
subp = subprocess.run(self.NMCLI_BASE + args,
capture_output=True, text=True)
try:
subp = subprocess.check_output(self.NMCLI_BASE + args).decode("utf-8")
except subprocess.CalledProcessError as err:
subp = err.output.decode("utf-8")
if parse:
# if no output
if subp.stdout.strip() == "":
if subp.strip() == "":
return []
return [
[field.replace("\\:", ":")
for field in re.split(r"(?<!\\):", line)]
for line in subp.stdout.strip().split("\n")]
for line in subp.strip().split("\n")]
return subp.stdout
return subp
def status(self):
infs = self._call_nmcli(["--fields", "TYPE,NAME",

View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._map import fxckers_map
__all__ = ['fxckers_map']

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import requests
from ..utils import get_user_agent, absolute_url

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import BaseFxcker

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._dummy import DummyFxcker
from .umwarszawa import UMWarszawaFxcker
from .ledatel import LedatelFxcker
@ -16,3 +19,5 @@ fxckers_map = [
"_PKP_WIFI",
], JustWifiFxcker),
]
__all__ = ['fxckers_map']

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import BaseFxcker
from ..utils import regex_search_string, dump_qs

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import BaseFxcker
from ..utils import regex_search_string, absolute_url, dump_qs

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import BaseFxcker
from ..utils import regex_search_string

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import BaseFxcker

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import BaseFxcker
from ..utils import regex_search_string, dump_qs

View File

@ -1,12 +1,14 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
import requests
from .connectors import get_connector
from .fxckers._map import fxckers_map
from .logger import _Logger, LOG_LEVELS
import re
import requests
class LibreFi:
def __init__(self, log_level=LOG_LEVELS.INFO, logger=_Logger):
@ -47,6 +49,7 @@ class LibreFi:
self._assign_fxcker_to_network(
{"ssid": status["connection_name"]})
if self.current_fxcker:
self.log.debug("Checking internet access")
check_req = requests.get(
"http://detectportal.firefox.com/success.txt",
allow_redirects=False)
@ -63,9 +66,9 @@ class LibreFi:
fxcker = None
for fxck_element in fxckers_map:
for fxck_net_name in fxck_element[0]:
if fxck_net_name[:3] == "re:":
if re.fullmatch(fxck_net_name,
"re:" + network["ssid"]):
if fxck_net_name[:len("re:")] == "re:":
if re.match(fxck_net_name,
network["ssid"][len("re:"):]):
fxcker = fxck_element[1]
break
elif fxck_net_name == network["ssid"]:

View File

@ -1,3 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class LOG_LEVELS:
DEBUG = -10
INFO = 0
@ -6,7 +10,8 @@ class LOG_LEVELS:
class _Logger:
printer = print
def printer(self, x):
print(x)
def __init__(self, key="--no-key--", log_level=LOG_LEVELS.INFO):
self.KEY = key

View File

@ -1,10 +1,27 @@
# -*- coding: utf-8 -*-
# flake8: noqa: E501
from __future__ import unicode_literals
import re
from urllib.parse import parse_qs, quote as qs_quote, urlparse
from datetime import datetime
import random
from .compat import (
compat_parse_qs as parse_qs,
compat_urllib_parse_quote as qs_quote,
compat_urllib_parse_urlparse as urlparse,
compat_str,
py_major_ver,
)
class LFiError(Exception):
pass
class FxckerError(Exception):
pass
def get_user_agent():
if bool(random.getrandbits(1)):
@ -112,15 +129,19 @@ def regex_search_string(regexes, string, default=None, multiple=False, whole_mat
def dump_qs(obj):
old_qs = []
if py_major_ver == 2:
old_qs = obj.iteritems()
else:
old_qs = list(obj.items())
# sorting by key to unify the result string between python versions
# https://stackoverflow.com/a/3295662/8222484
old_qs.sort()
qs = []
for key in obj:
old_qs.append((key, obj[key]))
not_flat = True
while not_flat:
not_flat = False
for old_qs_element in old_qs:
if isinstance(old_qs_element[1], (str, int, float)):
if isinstance(old_qs_element[1], (compat_str, int, float)):
qs.append((old_qs_element[0], old_qs_element[1]))
elif isinstance(old_qs_element[1], (dict)):
for subkey in old_qs_element[1]:
@ -134,7 +155,7 @@ def dump_qs(obj):
element = old_qs_element[1][index]
if element is not None:
qs.append(
(old_qs_element[0] + "[" + str(index) + "]", element))
(old_qs_element[0] + "[" + compat_str(index) + "]", element))
if isinstance(element, (dict, list)):
not_flat = True
if not_flat:
@ -142,12 +163,12 @@ def dump_qs(obj):
qs = []
strng = ""
for el in qs:
strng += qs_quote(str(el[0]), encoding="utf8") + "=" + \
qs_quote(str(el[1]), encoding="utf8") + "&"
strng += qs_quote(compat_str(el[0]).encode("utf-8")) + "=" + \
qs_quote(compat_str(el[1]).encode("utf-8")) + "&"
return strng[:-1]
def absolute_url(new_url: str, old_url: str):
def absolute_url(new_url, old_url):
if new_url.startswith("http:") or new_url.startswith("https:"):
return new_url

3
librefi/version.py Normal file
View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
__version__ = '2020.11.28'

6
setup.cfg Normal file
View File

@ -0,0 +1,6 @@
[wheel]
universal = True
[flake8]
exclude = librefi/compat.py,setup.py
ignore = E501

111
setup.py Normal file
View File

@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import io
import os
import sys
from shutil import rmtree
from setuptools import find_packages, setup, Command
NAME = 'librefi'
DESCRIPTION = 'LibreFi logs into public Wi-Fis without user interaction. Just access the network!'
URL = 'https://git.sakamoto.pl/laudom/librefi'
EMAIL = 'librefi@selfisekai.rocks'
AUTHOR = 'Lauren Liberda'
REQUIRES_PYTHON = '==2.7, >=3.8'
REQUIRED = [
'requests',
]
EXTRAS = {
# 'traffic debugging': ['mitmproxy'],
}
# Get the version without importing the package (copied from youtube-dl)
exec(compile(open('librefi/version.py').read(),
'librefi/version.py', 'exec'))
VERSION = __version__
try:
with io.open('README.md', encoding='utf-8') as f:
long_description = '\n' + f.read()
except FileNotFoundError:
long_description = DESCRIPTION
class UploadCommand(Command):
"""Support setup.py upload."""
description = 'Build and publish the package.'
user_options = []
@staticmethod
def status(s):
"""Prints things in bold."""
print('\033[1m%s\033[0m' % (s))
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
try:
self.status('Removing previous builds…')
rmtree(os.path.join(here, 'dist'))
except OSError:
pass
self.status('Building Source and Wheel (universal) distribution…')
os.system('%s setup.py sdist bdist_wheel --universal' % (sys.executable))
self.status('Uploading the package to PyPI via Twine…')
os.system('twine upload dist/*')
self.status('Pushing git tags…')
os.system('git tag v%s' % (VERSION))
os.system('git push v%s' % (VERSION))
sys.exit()
# Where the magic happens:
setup(
name=NAME,
version=VERSION,
description=DESCRIPTION,
long_description=long_description,
long_description_content_type='text/markdown',
author=AUTHOR,
author_email=EMAIL,
python_requires=REQUIRES_PYTHON,
url=URL,
py_modules=['librefi'],
entry_points={
'console_scripts': ['librefi=librefi:cli'],
},
install_requires=REQUIRED,
extras_require=EXTRAS,
include_package_data=True,
license='GPL-3.0-or-later',
classifiers=[
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: Implementation :: CPython',
],
# $ setup.py publish support.
cmdclass={
'upload': UploadCommand,
},
)

19
tests/py2_compat.py Normal file
View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from os import listdir, path
import re
def test_fxcker_files_py2_compatibility():
dir = path.join('.', 'librefi', 'fxckers')
fxckers = listdir(dir)
for fxcker in fxckers:
if re.match(r'^\w+\.py$', fxcker):
with open(path.join(dir, fxcker), 'r') as file:
content = file.read()
if re.match(r'^(#!.+\r?\n)?\s*(# (-\*- )?coding: utf-8( -\*-)?\r?\n)?\s*(from __future__ import (\w+, )*unicode_literals)',
content) is None:
print(content)
print(fxcker)
raise AssertionError('fxcker %s not containing required compat imports' % (fxcker))

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from librefi.utils import dump_qs
from librefi.compat import compat_str
from hashlib import sha512
@ -12,7 +16,7 @@ def test_dump():
{"przestań mi": ["kurwa", "rodzinę prześladować"]}],
}
result = dump_qs(obj)
assert isinstance(result, str)
assert isinstance(result, compat_str)
assert sha512(result.encode("utf-8")).hexdigest(
# flake8: noqa: E501
) == "80cb0feb585e8a5969598797b74c6f7f2f314ee97dfd2d6f4aaf431b800f6c7a1dfe77efd550a1009d41ef2886b21b87ce1d9e4f11444af554a916987344aee1"

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from librefi.utils import regex_search_string
from librefi.compat import compat_str
HTML_STRING = """
<form method="POST" action="/?your=mother">
@ -47,7 +51,7 @@ def test_regex_search_multiple_results():
assert isinstance(results, list)
assert len(results) == len(EXPECTED_RESULT_2)
for i in range(len(results)):
assert isinstance(results[i], str)
assert isinstance(results[i], compat_str)
assert results[i] == EXPECTED_RESULT_2[i]

View File

@ -1,10 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from librefi.utils import get_user_agent, get_email_address
from librefi.compat import compat_str
import re
def test_email_address():
email = get_email_address()
assert isinstance(email, str)
assert isinstance(email, compat_str)
assert re.search(
# intentionally dumb and not covering a lot of actual emails
r"^[a-zA-Z\d](?:[a-zA-Z\d._-]*[a-zA-Z\d])?@[a-z-\d]+(?:\.[a-z-\d]+)+$",
@ -14,5 +18,5 @@ def test_email_address():
def test_user_agent():
ua = get_user_agent()
assert isinstance(ua, str)
assert isinstance(ua, compat_str)
pass

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from librefi.utils import absolute_url
from librefi.compat import compat_str
OLD_URL = "https://sakamoto.pl/ddd"
NEW_URL_RELATIVE = "/DDD?test=yes"
@ -9,7 +13,7 @@ NEW_URL_ABSOLUTE = "https://sakamoto.pl/DDD?test=yes"
def test_basic():
abso = absolute_url(NEW_URL_RELATIVE, OLD_URL)
assert isinstance(abso, (str))
assert isinstance(abso, (compat_str))
print(abso)
assert abso == NEW_URL_ABSOLUTE
pass

5
tox.ini Normal file
View File

@ -0,0 +1,5 @@
[tox]
envlist = py27,py38,py39
[testenv]
deps = nose