From cabbef3c6cb4d7b08670f3d9ea7c658242f9c7d7 Mon Sep 17 00:00:00 2001 From: Aaron Robson Date: Fri, 28 May 2021 02:30:31 +0100 Subject: [PATCH] pep8-naming and mypy checks (and drop python2 support) --- .circleci/config.yml | 19 ++-- .travis.yml | 8 +- Makefile | 20 ++++- baseconv.py | 78 ++++++++--------- baseconvgui.py | 191 +++++++++++++++++++++-------------------- dev-requirements.txt | 3 + setup.cfg | 3 + tests/requirements.txt | 0 tests/test_gui.py | 6 +- tests/test_library.py | 48 +++++------ 10 files changed, 197 insertions(+), 179 deletions(-) create mode 100644 dev-requirements.txt create mode 100644 setup.cfg create mode 100644 tests/requirements.txt diff --git a/.circleci/config.yml b/.circleci/config.yml index c626517..e5a2220 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,7 +16,7 @@ jobs: # Download and cache dependencies - restore_cache: keys: - - v1-dependencies-{{ checksum "requirements.txt" }} + - v1-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "dev-requirements.txt"}}-{{ checksum "tests/requirements.txt"}} # fallback to using the latest cache if no exact match is found - v1-dependencies- @@ -25,23 +25,24 @@ jobs: command: | python3 -m venv venv . venv/bin/activate - pip install -r requirements.txt + make install-packages - save_cache: paths: - ./venv - key: v1-dependencies-{{ checksum "requirements.txt" }} + key: v1-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "dev-requirements.txt"}}-{{ checksum "tests/requirements.txt"}} + + - run: + name: run checks + command: | + . venv/bin/activate + make check - # run tests! - # this example uses Django's built-in test-runner - # other common Python testing frameworks include pytest and nose - # https://pytest.org - # https://nose.readthedocs.io - run: name: run tests command: | . venv/bin/activate - python -m unittest discover + make test - store_artifacts: path: test-reports diff --git a/.travis.yml b/.travis.yml index 8c5a042..8ce2cd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,6 @@ language: python matrix: include: - - python: "2.7" - - python: pypy - python: "3.5" - python: "3.6" - python: "3.7" @@ -18,14 +16,14 @@ matrix: allow_failures: - python: "3.10" - python: nightly + - python: pypy3 install: - - pip install -r requirements.txt - - pip install flake8 + - make install-packages - pip install python-coveralls script: - - flake8 . + - make check - nosetests - coverage run --source=. -m unittest discover diff --git a/Makefile b/Makefile index 5ed0036..eb64521 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,26 @@ .PHONY: all all: check test +install-packages: + pip3 install --upgrade \ + -r dev-requirements.txt \ + -r requirements.txt \ + -r tests/requirements.txt + .PHONY: check -check: +check: lint typecheck + +.PHONY: lint +lint: flake8 . +.PHONY: typecheck +typecheck: + mypy . + .PHONY: test -test: +test: unittest + +.PHONY: unittest +unittest: python3 -m unittest diff --git a/baseconv.py b/baseconv.py index 35ecbef..f3b0668 100644 --- a/baseconv.py +++ b/baseconv.py @@ -2,8 +2,6 @@ from string import digits, ascii_uppercase -from six.moves import input - MINUS_SIGN = '-' ZERO = '0' @@ -43,7 +41,7 @@ class InvalidInternalValueError(BaseConvError): _value = 'Invalid Internal Value Error' -def _ValidNum(value): +def _valid_num(value): '''Return a string representing the number given validated to standard notation: no leading zeros (with optional minus sign) @@ -79,7 +77,7 @@ def _ValidNum(value): return ZERO -def _ValidBas(value): +def _valid_bas(value): '''Returns an int with a value of 2 or above or raises InvalidBaseError exception. ''' @@ -90,62 +88,62 @@ def _ValidBas(value): try: # nominal case - intValue = int(value) + int_value = int(value) except ValueError: raise InvalidBaseError else: - if MINIMUM_BASE <= intValue: + if MINIMUM_BASE <= int_value: # nominal case - return intValue + return int_value else: raise InvalidBaseError -def _ValToChar(inputNum): +def _val_to_char(input_num): try: - inputNum = int(inputNum) + input_num = int(input_num) except ValueError: raise InvalidInternalValueError # Negative indexes count backwards from the end of the list - if inputNum < 0: + if input_num < 0: raise InvalidInternalValueError try: - return ALLOWED_SYMBOLS[inputNum] + return ALLOWED_SYMBOLS[input_num] except IndexError: raise InvalidInternalValueError -def IntoDec(inNum, inBas): +def into_dec(in_num, in_bas): '''Returns an int. ''' - inNum = _ValidNum(inNum) - inBas = _ValidBas(inBas) + in_num = _valid_num(in_num) + in_bas = _valid_bas(in_bas) try: - return int(inNum, inBas) + return int(in_num, in_bas) except ValueError: raise InvalidInputBaseError -def FromDec(inNum, outBas): +def from_dec(in_num, out_bas): '''Is an error for inNum to not be an integer. ''' try: - inNum = int(_ValidNum(inNum)) + in_num = int(_valid_num(in_num)) except ValueError: raise InvalidNumberError - outBas = _ValidBas(outBas) + out_bas = _valid_bas(out_bas) - minused = inNum < 0 + minused = in_num < 0 if minused: - inNum *= -1 + in_num *= -1 values = [] - while (0 < inNum): - values.append(_ValToChar(inNum % outBas)) - inNum //= outBas + while (0 < in_num): + values.append(_val_to_char(in_num % out_bas)) + in_num //= out_bas value = ''.join(reversed(values)) @@ -155,48 +153,44 @@ def FromDec(inNum, outBas): return ZERO -def BasCalc(inNum, inBas=DEFAULT_BASE, outBas=DEFAULT_BASE): +def bas_calc(in_num, in_bas=DEFAULT_BASE, out_bas=DEFAULT_BASE): '''Given a number and its current base, returns the number with a new specified base. If a base is not given it is assumed to be base %d. ''' % (DEFAULT_BASE) - return FromDec(IntoDec(inNum, inBas), outBas) + return from_dec(into_dec(in_num, in_bas), out_bas) -def _CommandLine(args): +def _command_line(args): if 1 < len(args): for arg in args[1:]: if arg in ('-h', '--help'): print('This program converts integers which may be signed ' + 'between any two number bases %d and over.\n' + 'Inputs as follows:\n' + - 'inNum = the Input Number\n' + - 'inBas = the Input Base\n' + - 'outBas = the Output Base' % (MINIMUM_BASE)) - break - elif arg in ('-t', '--test'): - import test - test.RunTests() + 'in_num = the Input Number\n' + + 'in_bas = the Input Base\n' + + 'out_bas = the Output Base' % (MINIMUM_BASE)) break else: - print(BasCalc(*args[1:])) + print(bas_calc(*args[1:])) else: print('Base Converter') - exitVals = ('q', 'quit') + exit_vals = ('q', 'quit') + exit_prompt = '\nEnter any of the following values to exit: %s\nor press return to continue: ' % str(exit_vals) while True: try: print('Output Number: ' + - BasCalc(input('\nEnter an Input Number: ').strip(), - input('Enter an Input Base: ').strip(), - input('Enter an Output Base: ').strip())) + bas_calc( + input('\nEnter an Input Number: ').strip(), + input('Enter an Input Base: ').strip(), + input('Enter an Output Base: ').strip())) except (BaseConvError, ValueError) as e: print('Error: ', e) - if input('\nEnter any of the following values to exit: %s\n' + - 'or press return to continue: ' % - (str(exitVals))).strip().lower() in exitVals: + if input(exit_prompt).strip().lower() in exit_vals: break if __name__ == "__main__": from sys import argv - _CommandLine(argv) + _command_line(argv) diff --git a/baseconvgui.py b/baseconvgui.py index 2683a9f..40458e3 100644 --- a/baseconvgui.py +++ b/baseconvgui.py @@ -1,11 +1,7 @@ #!/usr/bin/python from string import ascii_letters, digits - -try: - import tkinter as tk -except ImportError: - import Tkinter as tk +import tkinter as tk import baseconv @@ -13,8 +9,8 @@ JUSTIFY_ENTRY = tk.RIGHT -def OnValidateGiven(toValidate, allowedChars): - return all(char in allowedChars for char in toValidate) +def on_validate_given(to_validate, allowed_chars): + return all(char in allowed_chars for char in to_validate) class GUI(tk.Tk): @@ -32,93 +28,100 @@ def initialise(self): # always on top (might be windows only) self.wm_attributes('-topmost', 1) - formatTuple = ('%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W') - vNumLett = (self.register(self.OnValidateNumberLetter),) + formatTuple - vNum = (self.register(self.OnValidateNumber),) + formatTuple - vReadOnly = (self.register(self.OnValidateReadOnly),) + formatTuple + format_tuple = ('%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W') + v_num_lett = (self.register(self.on_validate_number_letter),) + format_tuple + v_num = (self.register(self.on_validate_number),) + format_tuple + v_read_only = (self.register(self.on_validate_read_only),) + format_tuple - self.vInNum = tk.StringVar() + self.v_in_num = tk.StringVar() tk.Label(self, text='Input Number').pack(anchor=tk.W) - edtInNum = tk.Entry(self, - justify=JUSTIFY_ENTRY, - textvariable=self.vInNum, - validate=VALIDATION_CHOICE, - validatecommand=vNumLett) - edtInNum.pack(fill=tk.X) - edtInNum.focus_set() - - self.vInBase = tk.IntVar() - self.vInBase.set(baseconv.DEFAULT_BASE) + edt_in_num = tk.Entry( + self, + justify=JUSTIFY_ENTRY, + textvariable=self.v_in_num, + validate=VALIDATION_CHOICE, + validatecommand=v_num_lett) + edt_in_num.pack(fill=tk.X) + edt_in_num.focus_set() + + self.v_in_base = tk.IntVar() + self.v_in_base.set(baseconv.DEFAULT_BASE) tk.Label(self, text='Input Base').pack(anchor=tk.W) - tk.Entry(self, - justify=JUSTIFY_ENTRY, - textvariable=self.vInBase, - validate=VALIDATION_CHOICE, - validatecommand=vNum).pack(fill=tk.X) + tk.Entry( + self, + justify=JUSTIFY_ENTRY, + textvariable=self.v_in_base, + validate=VALIDATION_CHOICE, + validatecommand=v_num).pack(fill=tk.X) - self.vOutBase = tk.IntVar() - self.vOutBase.set(baseconv.DEFAULT_BASE) + self.v_out_base = tk.IntVar() + self.v_out_base.set(baseconv.DEFAULT_BASE) tk.Label(self, text='Output Base').pack(anchor=tk.W) - tk.Entry(self, - justify=JUSTIFY_ENTRY, - textvariable=self.vOutBase, - validate=VALIDATION_CHOICE, - validatecommand=vNum).pack(fill=tk.X) - - fCont = tk.Frame(self) - tk.Button(fCont, - text='Calculate', - command=self.Calculate, - underline=1).pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES) - tk.Button(fCont, - text='Swap Bases', - command=self.Swap, - underline=0).pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES) - tk.Button(fCont, - text='Copy', - command=self.Copy, - underline=0).pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES) - fCont.pack(fill=tk.X) - - self.vOutNum = tk.StringVar() + tk.Entry( + self, + justify=JUSTIFY_ENTRY, + textvariable=self.v_out_base, + validate=VALIDATION_CHOICE, + validatecommand=v_num).pack(fill=tk.X) + + f_cont = tk.Frame(self) + tk.Button( + f_cont, + text='Calculate', + command=self.calculate, + underline=1).pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES) + tk.Button( + f_cont, + text='Swap Bases', + command=self.swap, + underline=0).pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES) + tk.Button( + f_cont, + text='Copy', + command=self.copy, + underline=0).pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES) + f_cont.pack(fill=tk.X) + + self.v_out_num = tk.StringVar() tk.Label(self, text='Output Number').pack(anchor=tk.W) - self.edtOutNum = tk.Entry(self, - justify=JUSTIFY_ENTRY, - textvariable=self.vOutNum, - validate=VALIDATION_CHOICE, - validatecommand=vReadOnly) - self.edtOutNum.pack(fill=tk.X) + self.edt_out_num = tk.Entry( + self, + justify=JUSTIFY_ENTRY, + textvariable=self.v_out_num, + validate=VALIDATION_CHOICE, + validatecommand=v_read_only) + self.edt_out_num.pack(fill=tk.X) - self.bind('', self.Calculate) + self.bind('', self.calculate) - self.bind('', self.CancelEscapeEvent) - self.bind('', self.EscapeEvent) + self.bind('', self.cancel_escape_event) + self.bind('', self.escape_event) - self.bind('', self.Calculate) - self.bind('', self.Swap) - self.bind('', self.Copy) + self.bind('', self.calculate) + self.bind('', self.swap) + self.bind('', self.copy) # in case the caps lock is on (yes silly it works like this) - self.bind('', self.Calculate) - self.bind('', self.Swap) - self.bind('', self.Copy) + self.bind('', self.calculate) + self.bind('', self.swap) + self.bind('', self.copy) # draw all the controls like "Application.ProcessMessages" in delphi self.update_idletasks() # then set the newly generated window as the minimum size of the window self.minsize(self.winfo_reqwidth(), self.winfo_reqheight()) - def CancelEscapeEvent(self, event=None): + def cancel_escape_event(self, event=None): pass - def EscapeEvent(self, event=None): + def escape_event(self, event=None): self.destroy() - def OnValidateNumberLetter(self, d, i, P, s, S, v, V, W): + def on_validate_number_letter(self, d, i, p_upper, s, s_upper, v, v_upper, w_upper): '''Valid percent substitutions (from the Tk entry man page) %d = Type of action (1=insert, 0=delete, -1 for others) %i = index of char string to be inserted/deleted, or -1 @@ -134,58 +137,58 @@ def OnValidateNumberLetter(self, d, i, P, s, S, v, V, W): print('OnValidate:') print('d=%r' % (d)) print('i=%r' % (i)) - print('P=%r' % (P)) + print('P=%r' % (p_upper)) print('s=%r' % (s)) - print('S=%r' % (S)) + print('S=%r' % (s_upper)) print('v=%r' % (v)) - print('V=%r' % (V)) - print('W=%r' % (W)) + print('V=%r' % (v_upper)) + print('W=%r' % (w_upper)) """ + return on_validate_given(p_upper, digits+ascii_letters) - return OnValidateGiven(P, digits+ascii_letters) - - def OnValidateNumber(self, d, i, P, s, S, v, V, W): - return OnValidateGiven(P, digits) + def on_validate_number(self, d, i, p_upper, s, s_upper, v, v_upper, w_upper): + return on_validate_given(p_upper, digits) - def OnValidateReadOnly(self, d, i, P, s, S, v, V, W): + def on_validate_read_only(self, d, i, p_upper, s, s_upper, v, v_upper, w_upper): '''Regardless of input allow no user changes. ''' return False - def Calculate(self, event=None): + def calculate(self, event=None): try: - result = baseconv.BasCalc(self.vInNum.get(), - self.vInBase.get(), - self.vOutBase.get()) + result = baseconv.bas_calc( + self.v_in_num.get(), + self.v_in_base.get(), + self.v_out_base.get()) except (baseconv.BaseConvError) as e: result = e print(result) else: - # uses "result" variable instead of "vOutNum" tk variable as + # uses "result" variable instead of "v_out_num" tk variable as # otherwise this line would be temporally coupled to the setting # of that control's value, making coding awkward. - print('%r in base %r = %r in base %r' % (self.vInNum.get(), - self.vInBase.get(), + print('%r in base %r = %r in base %r' % (self.v_in_num.get(), + self.v_in_base.get(), result, - self.vOutBase.get())) + self.v_out_base.get())) - self.vOutNum.set(result) + self.v_out_num.set(result) # Telling it to validate again as programatically filling the box with # text means validation gets turned off which is used to # make it readonly. # http://coding.derkeiler.com/Archive/Tcl/comp.lang.tcl/2003-11/0618.html - self.edtOutNum.config(validate=VALIDATION_CHOICE) + self.edt_out_num.config(validate=VALIDATION_CHOICE) - def Swap(self, event=None): - vIn, vOut = self.vInBase.get(), self.vOutBase.get() + def swap(self, event=None): + v_in, v_out = self.v_in_base.get(), self.v_out_base.get() - self.vInBase.set(vOut) - self.vOutBase.set(vIn) + self.v_in_base.set(v_out) + self.v_out_base.set(v_in) - def Copy(self, event=None): + def copy(self, event=None): self.clipboard_clear() - self.clipboard_append(self.vOutNum.get()) + self.clipboard_append(self.v_out_num.get()) if __name__ == "__main__": diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..6bbda39 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,3 @@ +flake8 +pep8-naming +mypy diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..a0d589e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[flake8] +exclude = .git,__pycache__,./venv +max-line-length = 120 diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_gui.py b/tests/test_gui.py index 6e4dbe9..fa786c3 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -1,16 +1,16 @@ import unittest import string -from baseconvgui import OnValidateGiven +from baseconvgui import on_validate_given class OnValidateGivenTest(unittest.TestCase): def test_valid(self): - self.assertTrue(OnValidateGiven(string.digits[0], string.digits)) + self.assertTrue(on_validate_given(string.digits[0], string.digits)) def test_invalid(self): - self.assertFalse(OnValidateGiven('*', string.digits)) + self.assertFalse(on_validate_given('*', string.digits)) if __name__ == "__main__": diff --git a/tests/test_library.py b/tests/test_library.py index 048add0..99545e2 100644 --- a/tests/test_library.py +++ b/tests/test_library.py @@ -5,50 +5,50 @@ import hypothesis.strategies as st import string -from baseconv import IntoDec, FromDec, BasCalc, MINIMUM_BASE +from baseconv import into_dec, from_dec, bas_calc, MINIMUM_BASE class TestBaseConvClass(unittest.TestCase): - def testIntoDec(self): - self.assertEqual(IntoDec(10, 2), 2, 'from binary') - self.assertEqual(IntoDec(-10, 2), -2, 'from binary negative') - self.assertEqual(IntoDec(101010, 2), 42, 'from binary 42') - self.assertEqual(IntoDec('9', 10), 9, 'under boundary condition') + def test_into_dec(self): + self.assertEqual(into_dec(10, 2), 2, 'from binary') + self.assertEqual(into_dec(-10, 2), -2, 'from binary negative') + self.assertEqual(into_dec(101010, 2), 42, 'from binary 42') + self.assertEqual(into_dec('9', 10), 9, 'under boundary condition') - def testFromDec(self): - self.assertEqual(FromDec(15, 16), 'F', 'edge') - self.assertEqual(FromDec(-15, 16), '-F', 'edge minus') - self.assertEqual(FromDec(42, 2), '101010', 'to binary') + def test_from_dec(self): + self.assertEqual(from_dec(15, 16), 'F', 'edge') + self.assertEqual(from_dec(-15, 16), '-F', 'edge minus') + self.assertEqual(from_dec(42, 2), '101010', 'to binary') @given( number=st.integers(), base=st.integers( min_value=MINIMUM_BASE, max_value=len(string.ascii_uppercase))) - def test_convert_FromDec_is_reverted_by_IntoDec(self, number, base): - self.assertEqual(IntoDec(FromDec(number, base), base), number) + def test_convert_from_dec_is_reverted_by_into_dec(self, number, base): + self.assertEqual(into_dec(from_dec(number, base), base), number) - def testBasCalc(self): - self.assertEqual(BasCalc(101010, 2, 10), '42', 'binary to decimal') - self.assertEqual(BasCalc('101010', '2', '10'), '42', + def test_bas_calc(self): + self.assertEqual(bas_calc(101010, 2, 10), '42', 'binary to decimal') + self.assertEqual(bas_calc('101010', '2', '10'), '42', 'text binary to decimal') - self.assertEqual(BasCalc(42, 10, 2), '101010', 'decimal to binary') - self.assertEqual(BasCalc('101010', inBas=2), '42', + self.assertEqual(bas_calc(42, 10, 2), '101010', 'decimal to binary') + self.assertEqual(bas_calc('101010', in_bas=2), '42', 'default out base, named in base') - self.assertEqual(BasCalc(42, outBas=2), '101010', + self.assertEqual(bas_calc(42, out_bas=2), '101010', 'default in base, named out base') - self.assertEqual(BasCalc(15, inBas=6, outBas=4), '23', + self.assertEqual(bas_calc(15, in_bas=6, out_bas=4), '23', 'named in and out bases') - self.assertEqual(BasCalc('FF', 16, 2), '11111111', 'hex to binary') - self.assertEqual(BasCalc(11111111, 2, 16), 'FF', 'binary to hex') - self.assertEqual(BasCalc('400', 16, 10), '1024', 'hex to decimal') + self.assertEqual(bas_calc('FF', 16, 2), '11111111', 'hex to binary') + self.assertEqual(bas_calc(11111111, 2, 16), 'FF', 'binary to hex') + self.assertEqual(bas_calc('400', 16, 10), '1024', 'hex to decimal') - self.assertEqual(BasCalc('-567', 10, 10), '-567', + self.assertEqual(bas_calc('-567', 10, 10), '-567', 'minus number decimal to decimal') for i in range(1000): - self.assertEqual(BasCalc('DEADBEEF', 16, 2), + self.assertEqual(bas_calc('DEADBEEF', 16, 2), '11011110101011011011111011101111')