diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index dc1fc9ed..05e3f65d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,7 +13,7 @@ jobs: continue-on-error: ${{ matrix.python-version == 'pypy3' }} strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + python-version: [3.7, 3.8, 3.9, "3.10", pypy3] steps: - uses: actions/checkout@v2 with: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 256e5d85..062a711b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,17 @@ Changelog ========= +0.23 +---- + +General information: + +New features: + +Fixes: + +Support for Python 3.6 has been dropped. + 0.22 ---- diff --git a/bpython/autocomplete.py b/bpython/autocomplete.py index 911dbb84..7a65d1b3 100644 --- a/bpython/autocomplete.py +++ b/bpython/autocomplete.py @@ -343,13 +343,14 @@ def format(self, word: str) -> str: return after_last_dot(word) +def _safe_glob(pathname: str) -> Iterator[str]: + return glob.iglob(glob.escape(pathname) + "*") + + class FilenameCompletion(BaseCompletionType): def __init__(self, mode: AutocompleteModes = AutocompleteModes.SIMPLE): super().__init__(False, mode) - def safe_glob(self, pathname: str) -> Iterator[str]: - return glob.iglob(glob.escape(pathname) + "*") - def matches( self, cursor_offset: int, line: str, **kwargs: Any ) -> Optional[Set]: @@ -359,7 +360,7 @@ def matches( matches = set() username = cs.word.split(os.path.sep, 1)[0] user_dir = os.path.expanduser(username) - for filename in self.safe_glob(os.path.expanduser(cs.word)): + for filename in _safe_glob(os.path.expanduser(cs.word)): if os.path.isdir(filename): filename += os.path.sep if cs.word.startswith("~"): @@ -371,7 +372,6 @@ def locate(self, cursor_offset: int, line: str) -> Optional[LinePart]: return lineparts.current_string(cursor_offset, line) def format(self, filename: str) -> str: - filename.rstrip(os.sep).rsplit(os.sep)[-1] if os.sep in filename[:-1]: return filename[filename.rindex(os.sep, 0, -1) + 1 :] else: @@ -570,15 +570,15 @@ def matches( r = self.locate(cursor_offset, line) if r is None: return None - if argspec: - matches = { - f"{name}=" - for name in argspec[1][0] - if isinstance(name, str) and name.startswith(r.word) - } - matches.update( - name + "=" for name in argspec[1][4] if name.startswith(r.word) - ) + + matches = { + f"{name}=" + for name in argspec[1][0] + if isinstance(name, str) and name.startswith(r.word) + } + matches.update( + name + "=" for name in argspec[1][4] if name.startswith(r.word) + ) return matches if matches else None def locate(self, cursor_offset: int, line: str) -> Optional[LinePart]: diff --git a/bpython/curtsiesfrontend/interpreter.py b/bpython/curtsiesfrontend/interpreter.py index 7f7c2fcc..48ee15ba 100644 --- a/bpython/curtsiesfrontend/interpreter.py +++ b/bpython/curtsiesfrontend/interpreter.py @@ -55,7 +55,7 @@ def format(self, tokensource, outfile): for token, text in tokensource: while token not in self.f_strings: token = token.parent - o += "{}\x03{}\x04".format(self.f_strings[token], text) + o += f"{self.f_strings[token]}\x03{text}\x04" outfile.write(parse(o.rstrip())) diff --git a/bpython/curtsiesfrontend/repl.py b/bpython/curtsiesfrontend/repl.py index 6007f615..54b70780 100644 --- a/bpython/curtsiesfrontend/repl.py +++ b/bpython/curtsiesfrontend/repl.py @@ -2067,7 +2067,7 @@ def key_help_text(self): max_func = max(len(func) for func, key in pairs) return "\n".join( - "{} : {}".format(func.rjust(max_func), key) for func, key in pairs + f"{func.rjust(max_func)} : {key}" for func, key in pairs ) def get_session_formatted_for_file(self) -> str: diff --git a/bpython/formatter.py b/bpython/formatter.py index 9fdd43a5..9618979a 100644 --- a/bpython/formatter.py +++ b/bpython/formatter.py @@ -99,7 +99,7 @@ class BPythonFormatter(Formatter): def __init__(self, color_scheme, **options): self.f_strings = {} for k, v in theme_map.items(): - self.f_strings[k] = "\x01{}".format(color_scheme[v]) + self.f_strings[k] = f"\x01{color_scheme[v]}" if k is Parenthesis: # FIXME: Find a way to make this the inverse of the current # background colour @@ -114,7 +114,7 @@ def format(self, tokensource, outfile): while token not in self.f_strings: token = token.parent - o += "{}\x03{}\x04".format(self.f_strings[token], text) + o += f"{self.f_strings[token]}\x03{text}\x04" outfile.write(o.rstrip()) diff --git a/bpython/inspection.py b/bpython/inspection.py index 08ad47c9..e7ab0a15 100644 --- a/bpython/inspection.py +++ b/bpython/inspection.py @@ -267,7 +267,7 @@ def getfuncprops(func, f): return fprops -def is_eval_safe_name(string): +def is_eval_safe_name(string: str) -> bool: return all( part.isidentifier() and not keyword.iskeyword(part) for part in string.split(".") @@ -334,7 +334,7 @@ def get_argspec_from_signature(f): get_encoding_line_re = LazyReCompile(r"^.*coding[:=]\s*([-\w.]+).*$") -def get_encoding(obj): +def get_encoding(obj) -> str: """Try to obtain encoding information of the source of an object.""" for line in inspect.findsource(obj)[0][:2]: m = get_encoding_line_re.search(line) @@ -354,7 +354,7 @@ def get_encoding_file(fname: str) -> str: return "utf8" -def getattr_safe(obj: Any, name: str): +def getattr_safe(obj: Any, name: str) -> Any: """side effect free getattr (calls getattr_static).""" result = inspect.getattr_static(obj, name) # Slots are a MemberDescriptorType @@ -369,8 +369,3 @@ def hasattr_safe(obj: Any, name: str) -> bool: return True except AttributeError: return False - - -def get_source_unicode(obj): - """Returns a decoded source of object""" - return inspect.getsource(obj) diff --git a/bpython/repl.py b/bpython/repl.py index 32d08dbf..9ffeba32 100644 --- a/bpython/repl.py +++ b/bpython/repl.py @@ -649,7 +649,7 @@ def get_source_of_current_name(self): raise SourceNotFound(_("Nothing to get source of")) if inspection.is_eval_safe_name(line): obj = self.get_object(line) - return inspection.get_source_unicode(obj) + return inspect.getsource(obj) except (AttributeError, NameError) as e: msg = _("Cannot get source: %s") % (e,) except OSError as e: diff --git a/bpython/simpleeval.py b/bpython/simpleeval.py index c5e14cf5..3992a70f 100644 --- a/bpython/simpleeval.py +++ b/bpython/simpleeval.py @@ -203,7 +203,7 @@ def safe_getitem(obj, index): return obj[index] except (KeyError, IndexError): raise EvaluationError(f"can't lookup key {index!r} on {obj!r}") - raise ValueError("unsafe to lookup on object of type {}".format(type(obj))) + raise ValueError(f"unsafe to lookup on object of type {type(obj)}") def find_attribute_with_name(node, name): diff --git a/bpython/test/test_inspection.py b/bpython/test/test_inspection.py index 50be0c3a..fdbb959c 100644 --- a/bpython/test/test_inspection.py +++ b/bpython/test/test_inspection.py @@ -1,3 +1,4 @@ +import inspect import os import sys import unittest @@ -89,19 +90,13 @@ def test_get_encoding_utf8(self): self.assertEqual(inspection.get_encoding(encoding_utf8.foo), "utf-8") def test_get_source_ascii(self): - self.assertEqual( - inspection.get_source_unicode(encoding_ascii.foo), foo_ascii_only - ) + self.assertEqual(inspect.getsource(encoding_ascii.foo), foo_ascii_only) def test_get_source_utf8(self): - self.assertEqual( - inspection.get_source_unicode(encoding_utf8.foo), foo_non_ascii - ) + self.assertEqual(inspect.getsource(encoding_utf8.foo), foo_non_ascii) def test_get_source_latin1(self): - self.assertEqual( - inspection.get_source_unicode(encoding_latin1.foo), foo_non_ascii - ) + self.assertEqual(inspect.getsource(encoding_latin1.foo), foo_non_ascii) def test_get_source_file(self): path = os.path.join( diff --git a/doc/sphinx/source/contributing.rst b/doc/sphinx/source/contributing.rst index 76d3b940..54fd56c6 100644 --- a/doc/sphinx/source/contributing.rst +++ b/doc/sphinx/source/contributing.rst @@ -17,7 +17,7 @@ the time of day. Getting your development environment set up ------------------------------------------- -bpython supports Python 3.6 and newer. The code is compatible with all +bpython supports Python 3.7 and newer. The code is compatible with all supported versions. Using a virtual environment is probably a good idea. Create a virtual diff --git a/doc/sphinx/source/releases.rst b/doc/sphinx/source/releases.rst index 1f723f59..738c24ff 100644 --- a/doc/sphinx/source/releases.rst +++ b/doc/sphinx/source/releases.rst @@ -45,7 +45,7 @@ A checklist to perform some manual tests before a release: Check that all of the following work before a release: -* Runs under Python 3.6 - 3.9 +* Runs under Python 3.7 - 3.9 * Save * Rewind * Pastebin diff --git a/pyproject.toml b/pyproject.toml index 79a558e8..c7ef64f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ requires = [ [tool.black] line-length = 80 -target_version = ["py36"] +target_version = ["py37"] include = '\.pyi?$' exclude = ''' /( diff --git a/requirements.txt b/requirements.txt index a989bdf4..7f56dc0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ Pygments backports.cached-property; python_version < "3.8" curtsies >=0.3.5 cwcwidth -dataclasses; python_version < "3.7" greenlet pyxdg requests diff --git a/setup.cfg b/setup.cfg index 1efed779..96ef47be 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,7 +11,7 @@ classifiers = Programming Language :: Python :: 3 [options] -python_requires = >=3.6 +python_requires = >=3.7 packages = bpython bpython.curtsiesfrontend @@ -22,12 +22,12 @@ packages = install_requires = backports.cached-property; python_version < "3.8" curtsies >=0.3.5 - dataclasses; python_version < "3.7" cwcwidth greenlet pygments pyxdg requests + typing-extensions [options.extras_require] clipboard = pyperclip