Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop python 2 compatibility #282

Merged
merged 19 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ jobs:
matrix:
os:
- ubuntu-latest
python-version: [ '3.x', 'pypy-3.8', 'pypy-2.7' ]
# DISABLED: python-version: [ '3.x', '2.x' ]
# include:
# - os: macos-latest
# python-version: '3.x'
# - windows-latest
- macos-13
python-version:
- '3.12'
- '3.11'
- '3.10'
- '3.9'
- '3.8'
- 'pypy-3.8'

steps:
- uses: actions/checkout@v4
Expand All @@ -54,4 +58,4 @@ jobs:

- name: run acceptance tests
run: make acceptance
working-directory: python
working-directory: python
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
- [.NET] Replace TinyJson to System.Text.Json
- [.NET] Enable warnings as errors

### Removed
- [Python] Drop compatibility for python 2. Supported python versions are 3.8, 3.9, 3.10, 3.12

## [29.0.0] - 2024-08-12
### Added
- (i18n) Added Gujarati translation for "Rule" ([#249](https://github.com/cucumber/gherkin/pull/249))
Expand Down
13 changes: 4 additions & 9 deletions python/bin/gherkin
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
#!/usr/bin/env sh
# Use "make GHERKIN_PYTHON_VERSION=python2 ..." to use python2
if [ -z "$GHERKIN_PYTHON_VERSION" ];
then
if [ -x "$(command -v python)" ]
then
GHERKIN_PYTHON_VERSION=python
elif [ -x "$(command -v python3)" ]
if [ -x "$(command -v python3)" ]
then
GHERKIN_PYTHON_VERSION=python3
elif [ -x "$(command -v python2)" ]
elif [ -x "$(command -v python)" ]
then
GHERKIN_PYTHON_VERSION=python2
else
echo "Neiter python, python3 or python2 found on PATH, exiting"
GHERKIN_PYTHON_VERSION=python
echo "Neither python3 or python found on PATH, exiting"
exit 1
fi
fi
Expand Down
13 changes: 4 additions & 9 deletions python/bin/gherkin-generate-tokens
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
#!/usr/bin/env sh
# Use "make GHERKIN_PYTHON_VERSION=python2 ..." to use python2
if [ -z "$GHERKIN_PYTHON_VERSION" ];
then
if [ -x "$(command -v python)" ]
then
GHERKIN_PYTHON_VERSION=python
elif [ -x "$(command -v python3)" ]
if [ -x "$(command -v python3)" ]
then
GHERKIN_PYTHON_VERSION=python3
elif [ -x "$(command -v python2)" ]
elif [ -x "$(command -v python)" ]
then
GHERKIN_PYTHON_VERSION=python2
else
echo "Neiter python, python3 or python2 found on PATH, exiting"
GHERKIN_PYTHON_VERSION=python
echo "Neither python3 or python found on PATH, exiting"
exit 1
fi
fi
Expand Down
6 changes: 0 additions & 6 deletions python/bin/gherkin_generate_tokens.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import codecs
import os
import sys
if sys.version_info < (3, 0):
import codecs
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
from gherkin.token_scanner import TokenScanner
from gherkin.token_formatter_builder import TokenFormatterBuilder
from gherkin.parser import Parser

files = sys.argv[1:]
if sys.version_info < (3, 0) and os.name != 'nt': # for Python2 unless on Windows native
UTF8Writer = codecs.getwriter('utf8')
sys.stdout = UTF8Writer(sys.stdout)
parser = Parser(TokenFormatterBuilder())
for file in files:
scanner = TokenScanner(file)
Expand Down
9 changes: 3 additions & 6 deletions python/gherkin-python.razor
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,21 @@ RULE_TYPE = [
]


class ParserContext(object):
class ParserContext:
def __init__(self, token_scanner, token_matcher, token_queue, errors):
self.token_scanner = token_scanner
self.token_matcher = token_matcher
self.token_queue = token_queue
self.errors = errors


class @(Model.ParserClassName)(object):
class @(Model.ParserClassName):
def __init__(self, ast_builder=None):
self.ast_builder = ast_builder if ast_builder is not None else AstBuilder()
self.stop_at_first_error = False

def parse(self, token_scanner_or_str, token_matcher=None):
if sys.version_info < (3, 0):
token_scanner = TokenScanner(token_scanner_or_str) if isinstance(token_scanner_or_str, basestring) else token_scanner_or_str
else:
token_scanner = TokenScanner(token_scanner_or_str) if isinstance(token_scanner_or_str, str) else token_scanner_or_str
token_scanner = TokenScanner(token_scanner_or_str) if isinstance(token_scanner_or_str, str) else token_scanner_or_str
self.ast_builder.reset()
if token_matcher is None:
token_matcher = TokenMatcher()
Expand Down
8 changes: 0 additions & 8 deletions python/gherkin/__main__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import os
from optparse import OptionParser
import sys
if sys.version_info < (3, 0):
string_type = basestring
if os.name != 'nt':
import codecs
UTF8Writer = codecs.getwriter('utf8')
sys.stdout = UTF8Writer(sys.stdout)
else:
string_type = str

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
import json
Expand Down
2 changes: 1 addition & 1 deletion python/gherkin/ast_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .errors import AstBuilderException
from .stream.id_generator import IdGenerator

class AstBuilder(object):
class AstBuilder:
def __init__(self, id_generator=None):
self.id_generator = id_generator
if self.id_generator is None:
Expand Down
2 changes: 1 addition & 1 deletion python/gherkin/ast_node.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections import defaultdict


class AstNode(object):
class AstNode:

def __init__(self, rule_type):
self.rule_type = rule_type
Expand Down
6 changes: 0 additions & 6 deletions python/gherkin/count_symbols.py

This file was deleted.

6 changes: 0 additions & 6 deletions python/gherkin/count_symbols_py2.py

This file was deleted.

2 changes: 0 additions & 2 deletions python/gherkin/count_symbols_py3_plus.py

This file was deleted.

4 changes: 2 additions & 2 deletions python/gherkin/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
os.path.dirname(__file__),
'gherkin-languages.json')

with io.open(DIALECT_FILE_PATH, 'r', encoding='utf-8') as file:
with open(DIALECT_FILE_PATH, encoding='utf-8') as file:
DIALECTS = json.load(file)


class Dialect(object):
class Dialect:

@classmethod
def for_name(cls, name):
Expand Down
10 changes: 5 additions & 5 deletions python/gherkin/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ class ParserError(Exception):
class ParserException(ParserError):
def __init__(self, message, location):
self.location = location
super(ParserException, self).__init__('(' + str(location['line']) + ':' +
super().__init__('(' + str(location['line']) + ':' +
str(location['column'] if 'column' in
location else 0) + '): ' + message)


class NoSuchLanguageException(ParserException):
def __init__(self, language, location):
super(NoSuchLanguageException, self).__init__('Language not supported: ' + language,
super().__init__('Language not supported: ' + language,
location)


Expand All @@ -23,7 +23,7 @@ class AstBuilderException(ParserException):
class UnexpectedEOFException(ParserException):
def __init__(self, received_token, expected_token_types, state_comment):
message = 'unexpected end of file, expected: ' + ', '.join(expected_token_types)
super(UnexpectedEOFException, self).__init__(message, received_token.location)
super().__init__(message, received_token.location)


class UnexpectedTokenException(ParserException):
Expand All @@ -34,12 +34,12 @@ def __init__(self, received_token, expected_token_types, state_comment):
location = (received_token.location if column else
{'line': received_token.location['line'],
'column': received_token.line.indent + 1})
super(UnexpectedTokenException, self).__init__(message, location)
super().__init__(message, location)


class CompositeParserException(ParserError):
def __init__(self, errors):
self.errors = errors
super(CompositeParserException, self).__init__("Parser errors:\n" +
super().__init__("Parser errors:\n" +
'\n'.join([error.args[0] for error in
errors]))
2 changes: 1 addition & 1 deletion python/gherkin/gherkin_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .errors import ParserException


class GherkinLine(object):
class GherkinLine:
def __init__(self, line_text, line_number):
self._line_text = line_text
self._line_number = line_number
Expand Down
3 changes: 1 addition & 2 deletions python/gherkin/inout.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from __future__ import print_function
import json
from .parser import Parser
from .token_scanner import TokenScanner
from .pickles.compiler import compile
from .errors import ParserException, CompositeParserException

class Inout(object):
class Inout:
def __init__(self, print_source, print_ast, print_pickles):
self.print_source = print_source
self.print_ast = print_ast
Expand Down
9 changes: 3 additions & 6 deletions python/gherkin/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,21 @@
]


class ParserContext(object):
class ParserContext:
def __init__(self, token_scanner, token_matcher, token_queue, errors):
self.token_scanner = token_scanner
self.token_matcher = token_matcher
self.token_queue = token_queue
self.errors = errors


class Parser(object):
class Parser:
def __init__(self, ast_builder=None):
self.ast_builder = ast_builder if ast_builder is not None else AstBuilder()
self.stop_at_first_error = False

def parse(self, token_scanner_or_str, token_matcher=None):
if sys.version_info < (3, 0):
token_scanner = TokenScanner(token_scanner_or_str) if isinstance(token_scanner_or_str, basestring) else token_scanner_or_str
else:
token_scanner = TokenScanner(token_scanner_or_str) if isinstance(token_scanner_or_str, str) else token_scanner_or_str
token_scanner = TokenScanner(token_scanner_or_str) if isinstance(token_scanner_or_str, str) else token_scanner_or_str
self.ast_builder.reset()
if token_matcher is None:
token_matcher = TokenMatcher()
Expand Down
6 changes: 2 additions & 4 deletions python/gherkin/pickles/compiler.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import re

from ..count_symbols import count_symbols
from ..stream.id_generator import IdGenerator


class Compiler(object):
class Compiler:
def __init__(self, id_generator=None):
self.id_generator = id_generator
if self.id_generator is None:
Expand Down Expand Up @@ -158,7 +156,7 @@ def _interpolate(self, name, variable_cells, value_cells):
# For the case of trailing backslash, re-escaping backslashes are needed
reescaped_value = re.sub(r'\\', r'\\\\', value_cell['value'])
name = re.sub(
u'<{0[value]}>'.format(variable_cell),
'<{0[value]}>'.format(variable_cell),
reescaped_value,
name
)
Expand Down
6 changes: 2 additions & 4 deletions python/gherkin/stream/gherkin_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ def enum(self, source_event):
'pickle': pickle
}
except CompositeParserException as e:
for event in create_errors(e.errors, uri):
yield event
yield from create_errors(e.errors, uri)
except ParserError as e:
for event in create_errors([e], uri):
yield event
yield from create_errors([e], uri)
2 changes: 1 addition & 1 deletion python/gherkin/stream/id_generator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class IdGenerator(object):
class IdGenerator:
def __init__(self):
self._id_counter = 0

Expand Down
4 changes: 1 addition & 3 deletions python/gherkin/stream/source_events.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import io

def source_event(path):
event = {
'source': {
'uri': path,
'data': io.open(path, 'r', encoding='utf8', newline='').read(),
'data': open(path, encoding='utf8', newline='').read(),
'mediaType': 'text/x.cucumber.gherkin+plain'
}
}
Expand Down
2 changes: 1 addition & 1 deletion python/gherkin/token.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class Token(object):
class Token:
def __init__(self, gherkin_line, location):
self.line = gherkin_line
self.location = location
Expand Down
15 changes: 2 additions & 13 deletions python/gherkin/token_matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,8 @@
from .dialect import Dialect
from .errors import NoSuchLanguageException

# Source: https://stackoverflow.com/a/8348914
try:
import textwrap
textwrap.indent
except AttributeError: # undefined function (wasn't added until Python 3.3)
def indent(text, amount, ch=' '):
padding = amount * ch
return ''.join(padding+line for line in text.splitlines(True))
else:
def indent(text, amount, ch=' '):
return textwrap.indent(text, amount * ch)

class TokenMatcher(object):

class TokenMatcher:
LANGUAGE_RE = re.compile(r"^\s*#\s*language\s*:\s*([a-zA-Z\-_]+)\s*$")

def __init__(self, dialect_name='en'):
Expand Down
6 changes: 3 additions & 3 deletions python/gherkin/token_matcher_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
class GherkinInMarkdownTokenMatcher(TokenMatcher):

def __init__(self, dialect_name='en'):
super(GherkinInMarkdownTokenMatcher, self).__init__(dialect_name)
super().__init__(dialect_name)

def reset(self):
super(GherkinInMarkdownTokenMatcher, self).reset()
super().reset()
self.matched_feature_line=False

def match_FeatureLine(self, token):
Expand Down Expand Up @@ -142,7 +142,7 @@ def _default_docstring_content_type():
def _match_title_line(self, prefix, keywords, keywordSuffix, token, token_type):

keywords_or_list="|".join(map(lambda x: re.escape(x), keywords))
match = re.search(u'{}({}){}(.*)'.format(prefix, keywords_or_list, keywordSuffix), token.line.get_line_text())
match = re.search(f'{prefix}({keywords_or_list}){keywordSuffix}(.*)', token.line.get_line_text())
indent = token.line.indent

if(match):
Expand Down
Loading
Loading