diff --git a/Makefile b/Makefile
index 64b9b22..c115c66 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,5 @@
+all: typecheck test README.rst
+
 README.rst: README.md
 	pandoc --from=markdown --to=rst $< > $@
 
@@ -8,4 +10,7 @@ test:
 	python src/braceexpand/__init__.py
 	python test_braceexpand.py
 
-.PHONY: test
+typecheck:
+	mypy --strict src/braceexpand/__init__.py
+
+.PHONY: all test typecheck
diff --git a/PKG-INFO b/PKG-INFO
index 5dd2ce2..94a7772 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,4 +1,4 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: braceexpand
 Version: 0.1.7
 Summary: Bash-style brace expansion for Python
@@ -6,103 +6,106 @@ Home-page: https://github.com/trendels/braceexpand
 Author: Stanis Trendelenburg
 Author-email: stanis.trendelenburg@gmail.com
 License: MIT
-Description: Bash-style brace expansion for Python
-        =====================================
-        
-        |build-status-img| |PyPI|
-        
-        Implements Brace Expansion as described in
-        `bash(1) <http://man7.org/linux/man-pages/man1/bash.1.html#EXPANSION>`__,
-        with the following limitations:
-        
-        -  A pattern containing unbalanced braces will raise an
-           ``UnbalancedBracesError`` exception. In bash, unbalanced braces will
-           either be partly expanded or ignored.
-        
-        -  A mixed-case character range like ``'{Z..a}'`` or ``'{a..Z}'`` will
-           not include the characters :literal:`[]^_\`` between ``Z`` and ``a``.
-        
-        ``braceexpand`` is tested with Python 2.7, and 3.6+
-        
-        Installation
-        ------------
-        
-        Install the ``braceexpand`` package from pypi:
-        
-        ::
-        
-           $ pip install braceexpand
-        
-        Examples
-        --------
-        
-        The ``braceexpand`` function returns an iterator over the expansions
-        generated from a pattern.
-        
-        .. code:: python
-        
-           >>> from braceexpand import braceexpand
-        
-           # Integer range
-           >>> list(braceexpand('item{1..3}'))
-           ['item1', 'item2', 'item3']
-        
-           # Character range
-           >>> list(braceexpand('{a..c}'))
-           ['a', 'b', 'c']
-        
-           # Sequence
-           >>> list(braceexpand('index.html{,.backup}'))
-           ['index.html', 'index.html.backup']
-        
-           # Nested patterns
-           >>> list(braceexpand('python{2.{5..7},3.{2,3}}'))
-           ['python2.5', 'python2.6', 'python2.7', 'python3.2', 'python3.3']
-        
-           # Prefixing an integer with zero causes all numbers to be padded to
-           # the same width.
-           >>> list(braceexpand('{07..10}'))
-           ['07', '08', '09', '10']
-        
-           # An optional increment can be specified for ranges.
-           >>> list(braceexpand('{a..g..2}'))
-           ['a', 'c', 'e', 'g']
-        
-           # Ranges can go in both directions.
-           >>> list(braceexpand('{4..1}'))
-           ['4', '3', '2', '1']
-        
-           # Numbers can be negative
-           >>> list(braceexpand('{2..-1}'))
-           ['2', '1', '0', '-1']
-        
-           # Unbalanced braces raise an exception.
-           >>> list(braceexpand('{1{2,3}'))
-           Traceback (most recent call last):
-               ...
-           UnbalancedBracesError: Unbalanced braces: '{1{2,3}'
-        
-           # By default, the backslash is the escape character.
-           >>> list(braceexpand(r'{1\{2,3}'))
-           ['1{2', '3']
-        
-           # Setting 'escape' to False disables backslash escaping.
-           >>> list(braceexpand(r'\{1,2}', escape=False))
-           ['\\1', '\\2']
-        
-        License
-        -------
-        
-        braceexpand is licensed under the MIT License. See the included file
-        ``LICENSE`` for details.
-        
-        .. |build-status-img| image:: https://travis-ci.org/trendels/braceexpand.svg
-           :target: https://travis-ci.org/trendels/braceexpand
-        .. |PyPI| image:: https://img.shields.io/pypi/v/braceexpand
-           :target: https://pypi.python.org/pypi/braceexpand
-        
 Platform: UNKNOWN
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 3
+License-File: LICENSE
+
+Bash-style brace expansion for Python
+=====================================
+
+|build-status-img| |PyPI|
+
+Implements Brace Expansion as described in
+`bash(1) <http://man7.org/linux/man-pages/man1/bash.1.html#EXPANSION>`__,
+with the following limitations:
+
+-  A pattern containing unbalanced braces will raise an
+   ``UnbalancedBracesError`` exception. In bash, unbalanced braces will
+   either be partly expanded or ignored.
+
+-  A mixed-case character range like ``'{Z..a}'`` or ``'{a..Z}'`` will
+   not include the characters :literal:`[]^_\`` between ``Z`` and ``a``.
+
+``braceexpand`` is tested with Python 2.7, and 3.6+
+
+Installation
+------------
+
+Install the ``braceexpand`` package from pypi:
+
+::
+
+   $ pip install braceexpand
+
+Examples
+--------
+
+The ``braceexpand`` function returns an iterator over the expansions
+generated from a pattern.
+
+.. code:: python
+
+   >>> from braceexpand import braceexpand
+
+   # Integer range
+   >>> list(braceexpand('item{1..3}'))
+   ['item1', 'item2', 'item3']
+
+   # Character range
+   >>> list(braceexpand('{a..c}'))
+   ['a', 'b', 'c']
+
+   # Sequence
+   >>> list(braceexpand('index.html{,.backup}'))
+   ['index.html', 'index.html.backup']
+
+   # Nested patterns
+   >>> list(braceexpand('python{2.{5..7},3.{2,3}}'))
+   ['python2.5', 'python2.6', 'python2.7', 'python3.2', 'python3.3']
+
+   # Prefixing an integer with zero causes all numbers to be padded to
+   # the same width.
+   >>> list(braceexpand('{07..10}'))
+   ['07', '08', '09', '10']
+
+   # An optional increment can be specified for ranges.
+   >>> list(braceexpand('{a..g..2}'))
+   ['a', 'c', 'e', 'g']
+
+   # Ranges can go in both directions.
+   >>> list(braceexpand('{4..1}'))
+   ['4', '3', '2', '1']
+
+   # Numbers can be negative
+   >>> list(braceexpand('{2..-1}'))
+   ['2', '1', '0', '-1']
+
+   # Unbalanced braces raise an exception.
+   >>> list(braceexpand('{1{2,3}'))
+   Traceback (most recent call last):
+       ...
+   UnbalancedBracesError: Unbalanced braces: '{1{2,3}'
+
+   # By default, the backslash is the escape character.
+   >>> list(braceexpand(r'{1\{2,3}'))
+   ['1{2', '3']
+
+   # Setting 'escape' to False disables backslash escaping.
+   >>> list(braceexpand(r'\{1,2}', escape=False))
+   ['\\1', '\\2']
+
+License
+-------
+
+braceexpand is licensed under the MIT License. See the included file
+``LICENSE`` for details.
+
+.. |build-status-img| image:: https://travis-ci.org/trendels/braceexpand.svg
+   :target: https://travis-ci.org/trendels/braceexpand
+.. |PyPI| image:: https://img.shields.io/pypi/v/braceexpand
+   :target: https://pypi.python.org/pypi/braceexpand
+
+
diff --git a/debian/changelog b/debian/changelog
index f4ce4fd..f72d05c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+braceexpand (0.1.7+git20211008.1.334cdab+ds-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 01 Aug 2022 09:16:36 -0000
+
 braceexpand (0.1.7-3) unstable; urgency=medium
 
   * Really remove braceexpand.egg-info during clean target.
diff --git a/setup.py b/setup.py
index 295c2a6..0fb2213 100644
--- a/setup.py
+++ b/setup.py
@@ -27,4 +27,7 @@ setup(
     package_dir={'': 'src'},
     packages=find_packages('src'),
     include_package_data=True,
+    install_requires=[
+        'typing;python_version<"3.5"'
+    ],
 )
diff --git a/src/braceexpand.egg-info/PKG-INFO b/src/braceexpand.egg-info/PKG-INFO
index 5dd2ce2..94a7772 100644
--- a/src/braceexpand.egg-info/PKG-INFO
+++ b/src/braceexpand.egg-info/PKG-INFO
@@ -1,4 +1,4 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: braceexpand
 Version: 0.1.7
 Summary: Bash-style brace expansion for Python
@@ -6,103 +6,106 @@ Home-page: https://github.com/trendels/braceexpand
 Author: Stanis Trendelenburg
 Author-email: stanis.trendelenburg@gmail.com
 License: MIT
-Description: Bash-style brace expansion for Python
-        =====================================
-        
-        |build-status-img| |PyPI|
-        
-        Implements Brace Expansion as described in
-        `bash(1) <http://man7.org/linux/man-pages/man1/bash.1.html#EXPANSION>`__,
-        with the following limitations:
-        
-        -  A pattern containing unbalanced braces will raise an
-           ``UnbalancedBracesError`` exception. In bash, unbalanced braces will
-           either be partly expanded or ignored.
-        
-        -  A mixed-case character range like ``'{Z..a}'`` or ``'{a..Z}'`` will
-           not include the characters :literal:`[]^_\`` between ``Z`` and ``a``.
-        
-        ``braceexpand`` is tested with Python 2.7, and 3.6+
-        
-        Installation
-        ------------
-        
-        Install the ``braceexpand`` package from pypi:
-        
-        ::
-        
-           $ pip install braceexpand
-        
-        Examples
-        --------
-        
-        The ``braceexpand`` function returns an iterator over the expansions
-        generated from a pattern.
-        
-        .. code:: python
-        
-           >>> from braceexpand import braceexpand
-        
-           # Integer range
-           >>> list(braceexpand('item{1..3}'))
-           ['item1', 'item2', 'item3']
-        
-           # Character range
-           >>> list(braceexpand('{a..c}'))
-           ['a', 'b', 'c']
-        
-           # Sequence
-           >>> list(braceexpand('index.html{,.backup}'))
-           ['index.html', 'index.html.backup']
-        
-           # Nested patterns
-           >>> list(braceexpand('python{2.{5..7},3.{2,3}}'))
-           ['python2.5', 'python2.6', 'python2.7', 'python3.2', 'python3.3']
-        
-           # Prefixing an integer with zero causes all numbers to be padded to
-           # the same width.
-           >>> list(braceexpand('{07..10}'))
-           ['07', '08', '09', '10']
-        
-           # An optional increment can be specified for ranges.
-           >>> list(braceexpand('{a..g..2}'))
-           ['a', 'c', 'e', 'g']
-        
-           # Ranges can go in both directions.
-           >>> list(braceexpand('{4..1}'))
-           ['4', '3', '2', '1']
-        
-           # Numbers can be negative
-           >>> list(braceexpand('{2..-1}'))
-           ['2', '1', '0', '-1']
-        
-           # Unbalanced braces raise an exception.
-           >>> list(braceexpand('{1{2,3}'))
-           Traceback (most recent call last):
-               ...
-           UnbalancedBracesError: Unbalanced braces: '{1{2,3}'
-        
-           # By default, the backslash is the escape character.
-           >>> list(braceexpand(r'{1\{2,3}'))
-           ['1{2', '3']
-        
-           # Setting 'escape' to False disables backslash escaping.
-           >>> list(braceexpand(r'\{1,2}', escape=False))
-           ['\\1', '\\2']
-        
-        License
-        -------
-        
-        braceexpand is licensed under the MIT License. See the included file
-        ``LICENSE`` for details.
-        
-        .. |build-status-img| image:: https://travis-ci.org/trendels/braceexpand.svg
-           :target: https://travis-ci.org/trendels/braceexpand
-        .. |PyPI| image:: https://img.shields.io/pypi/v/braceexpand
-           :target: https://pypi.python.org/pypi/braceexpand
-        
 Platform: UNKNOWN
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 3
+License-File: LICENSE
+
+Bash-style brace expansion for Python
+=====================================
+
+|build-status-img| |PyPI|
+
+Implements Brace Expansion as described in
+`bash(1) <http://man7.org/linux/man-pages/man1/bash.1.html#EXPANSION>`__,
+with the following limitations:
+
+-  A pattern containing unbalanced braces will raise an
+   ``UnbalancedBracesError`` exception. In bash, unbalanced braces will
+   either be partly expanded or ignored.
+
+-  A mixed-case character range like ``'{Z..a}'`` or ``'{a..Z}'`` will
+   not include the characters :literal:`[]^_\`` between ``Z`` and ``a``.
+
+``braceexpand`` is tested with Python 2.7, and 3.6+
+
+Installation
+------------
+
+Install the ``braceexpand`` package from pypi:
+
+::
+
+   $ pip install braceexpand
+
+Examples
+--------
+
+The ``braceexpand`` function returns an iterator over the expansions
+generated from a pattern.
+
+.. code:: python
+
+   >>> from braceexpand import braceexpand
+
+   # Integer range
+   >>> list(braceexpand('item{1..3}'))
+   ['item1', 'item2', 'item3']
+
+   # Character range
+   >>> list(braceexpand('{a..c}'))
+   ['a', 'b', 'c']
+
+   # Sequence
+   >>> list(braceexpand('index.html{,.backup}'))
+   ['index.html', 'index.html.backup']
+
+   # Nested patterns
+   >>> list(braceexpand('python{2.{5..7},3.{2,3}}'))
+   ['python2.5', 'python2.6', 'python2.7', 'python3.2', 'python3.3']
+
+   # Prefixing an integer with zero causes all numbers to be padded to
+   # the same width.
+   >>> list(braceexpand('{07..10}'))
+   ['07', '08', '09', '10']
+
+   # An optional increment can be specified for ranges.
+   >>> list(braceexpand('{a..g..2}'))
+   ['a', 'c', 'e', 'g']
+
+   # Ranges can go in both directions.
+   >>> list(braceexpand('{4..1}'))
+   ['4', '3', '2', '1']
+
+   # Numbers can be negative
+   >>> list(braceexpand('{2..-1}'))
+   ['2', '1', '0', '-1']
+
+   # Unbalanced braces raise an exception.
+   >>> list(braceexpand('{1{2,3}'))
+   Traceback (most recent call last):
+       ...
+   UnbalancedBracesError: Unbalanced braces: '{1{2,3}'
+
+   # By default, the backslash is the escape character.
+   >>> list(braceexpand(r'{1\{2,3}'))
+   ['1{2', '3']
+
+   # Setting 'escape' to False disables backslash escaping.
+   >>> list(braceexpand(r'\{1,2}', escape=False))
+   ['\\1', '\\2']
+
+License
+-------
+
+braceexpand is licensed under the MIT License. See the included file
+``LICENSE`` for details.
+
+.. |build-status-img| image:: https://travis-ci.org/trendels/braceexpand.svg
+   :target: https://travis-ci.org/trendels/braceexpand
+.. |PyPI| image:: https://img.shields.io/pypi/v/braceexpand
+   :target: https://pypi.python.org/pypi/braceexpand
+
+
diff --git a/src/braceexpand.egg-info/SOURCES.txt b/src/braceexpand.egg-info/SOURCES.txt
index 8990015..d677050 100644
--- a/src/braceexpand.egg-info/SOURCES.txt
+++ b/src/braceexpand.egg-info/SOURCES.txt
@@ -7,9 +7,9 @@ README.rst
 setup.py
 test_braceexpand.py
 src/braceexpand/__init__.py
-src/braceexpand/__init__.pyi
 src/braceexpand/py.typed
 src/braceexpand.egg-info/PKG-INFO
 src/braceexpand.egg-info/SOURCES.txt
 src/braceexpand.egg-info/dependency_links.txt
+src/braceexpand.egg-info/requires.txt
 src/braceexpand.egg-info/top_level.txt
\ No newline at end of file
diff --git a/src/braceexpand.egg-info/requires.txt b/src/braceexpand.egg-info/requires.txt
new file mode 100644
index 0000000..cae3b29
--- /dev/null
+++ b/src/braceexpand.egg-info/requires.txt
@@ -0,0 +1,3 @@
+
+[:python_version < "3.5"]
+typing
diff --git a/src/braceexpand/__init__.py b/src/braceexpand/__init__.py
index 1c76fcf..f498e63 100644
--- a/src/braceexpand/__init__.py
+++ b/src/braceexpand/__init__.py
@@ -3,6 +3,7 @@ import re
 import string
 import sys
 from itertools import chain, product
+from typing import Iterable, Iterator, List, Optional
 
 __version__ = '0.1.7'
 
@@ -19,9 +20,11 @@ alphabet = string.ascii_uppercase + string.ascii_lowercase
 
 int_range_re = re.compile(r'^(-?\d+)\.\.(-?\d+)(?:\.\.-?(\d+))?$')
 char_range_re = re.compile(r'^([A-Za-z])\.\.([A-Za-z])(?:\.\.-?(\d+))?$')
+escape_re = re.compile(r'\\(.)')
 
 
 def braceexpand(pattern, escape=True):
+    # type: (str, bool) -> Iterator[str]
     """braceexpand(pattern) -> iterator over generated strings
 
     Returns an iterator over the strings resulting from brace expansion
@@ -95,15 +98,16 @@ def braceexpand(pattern, escape=True):
     ['\\\\1', '\\\\2']
 
     """
-    return (_flatten(t, escape) for t in parse_pattern(pattern, escape))
+    return (escape_re.sub(r'\1', s) if escape else s for s in parse_pattern(pattern, escape))
 
 
 def parse_pattern(pattern, escape):
+    # type: (str, bool) -> Iterator[str]
     # pattern -> product(*parts)
     start = 0
     pos = 0
     bracketdepth = 0
-    items = []
+    items = []  # type: List[Iterable[str]]
 
     #print 'pattern:', pattern
     while pos < len(pattern):
@@ -136,10 +140,11 @@ def parse_pattern(pattern, escape):
         #print 'literal:', pattern[start:]
         items.append([pattern[start:]])
 
-    return product(*items)
+    return ("".join(item) for item in product(*items))
 
 
 def parse_expression(expr, escape):
+    # type: (str, bool) -> Optional[Iterable[str]]
     int_range_match = int_range_re.match(expr)
     if int_range_match:
         return make_int_range(*int_range_match.groups())
@@ -152,11 +157,12 @@ def parse_expression(expr, escape):
 
 
 def parse_sequence(seq, escape):
+    # type: (str, bool) -> Optional[Iterator[str]]
     # sequence -> chain(*sequence_items)
     start = 0
     pos = 0
     bracketdepth = 0
-    items = []
+    items = []  # type: List[Iterable[str]]
 
     #print 'sequence:', seq
     while pos < len(seq):
@@ -172,7 +178,9 @@ def parse_sequence(seq, escape):
             start = pos + 1 # skip the comma
         pos += 1
 
-    if bracketdepth != 0 or not items: # unbalanced braces or not a sequence
+    if bracketdepth != 0:
+        raise UnbalancedBracesError
+    if not items:
         return None
 
     # part after the last comma (may be the empty string)
@@ -180,25 +188,27 @@ def parse_sequence(seq, escape):
     return chain(*items)
 
 
-def make_int_range(start, end, step=None):
+def make_int_range(left, right, incr=None):
+    # type: (str, str, Optional[str]) -> Iterator[str]
     if any([s.startswith(('0', '-0'))
-            for s in (start, end) if s not in ('0', '-0')]):
-        padding = max(len(start), len(end))
+            for s in (left, right) if s not in ('0', '-0')]):
+        padding = max(len(left), len(right))
     else:
         padding = 0
-    step = (int(step) or 1) if step else 1
-    start = int(start)
-    end = int(end)
+    step = (int(incr) or 1) if incr else 1
+    start = int(left)
+    end = int(right)
     r = xrange(start, end+1, step) if start < end else \
         xrange(start, end-1, -step)
     fmt = '%0{}d'.format(padding)
     return (fmt % i for i in r)
 
 
-def make_char_range(start, end, step=None):
-    step = (int(step) or 1) if step else 1
-    start = alphabet.index(start)
-    end = alphabet.index(end)
+def make_char_range(left, right, incr=None):
+    # type: (str, str, Optional[str]) -> str
+    step = (int(incr) or 1) if incr else 1
+    start = alphabet.index(left)
+    end = alphabet.index(right)
     if start < end:
         return alphabet[start:end+1:step]
     else:
@@ -206,18 +216,6 @@ def make_char_range(start, end, step=None):
         return alphabet[start:end-1:-step]
 
 
-escape_re = re.compile(r'\\(.)')
-
-def _flatten(t, escape):
-    l = []
-    for item in t:
-        if isinstance(item, tuple): l.extend(_flatten(item, escape))
-        else: l.append(item)
-    s = ''.join(l)
-    # Strip escape characters from generated strings after expansion.
-    return escape_re.sub(r'\1', s) if escape else s
-
-
 if __name__ == '__main__':
     import doctest
     import sys
diff --git a/src/braceexpand/__init__.pyi b/src/braceexpand/__init__.pyi
deleted file mode 100644
index c254673..0000000
--- a/src/braceexpand/__init__.pyi
+++ /dev/null
@@ -1,6 +0,0 @@
-from typing import Iterator
-
-alphabet: str
-
-def braceexpand(pattern: str, escape: bool = ...) -> Iterator[str]: ...
-