Repository: r1chardj0n3s/parse

Issue:
Parsing a continous string
I was trying to parse continous string having letters and numbers by the easiest way:

>>> parse('{:>w}{:g}{:w}{:g}{:w}', ' G3.80XA5.2M')
<Result ('G', 3.8, 'XA', 5.2, 'M') {}>
>>> parse('{:>w}{:g}{:w}{:g}{:w}', ' G3.80XA4.2M')
<Result ('G', 3.8, 'XA', 4.2, 'M') {}>

as far so good, but 

>>> parse('{:>w}{:g}{:w}{:g}{:w}', ' G3.80XA04.2M')
<Result ('G', 3.8, 'XA0', 4.2, 'M') {}>
>>> parse('{:>w}{:g}{:w}{:g}{:w}', ' G3.80XA44.2M')
<Result ('G', 3.8, 'XA4', 4.2, 'M') {}>
>>> parse('{:>w}{:g}{:w}{:g}{:w}', ' G3.80XA40M')
<Result ('G', 3.8, 'XA4', 0.0, 'M') {}>

changing input string a little bit I was getting bad results. 

I am missing something?

Igor

Comments:

Patch:
diff --git a/LICENSE b/LICENSE
index b4d8e28..6c73b16 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2012-2018 Richard Jones <richard@python.org>
+Copyright (c) 2012-2019 Richard Jones <richard@python.org>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.rst b/README.rst
index a16654c..5445556 100644
--- a/README.rst
+++ b/README.rst
@@ -132,8 +132,9 @@ The differences between `parse()` and `format()` are:
 ===== =========================================== ========
 Type  Characters Matched                          Output
 ===== =========================================== ========
-w     Letters and underscore                      str
-W     Non-letter and underscore                   str
+l     Letters (ASCII)                             str
+w     Letters, numbers and underscore             str
+W     Not letters, numbers and underscore         str
 s     Whitespace                                  str
 S     Non-whitespace                              str
 d     Digits (effectively integer numbers)        int
@@ -342,6 +343,8 @@ the pattern, the actual match represents the shortest successful match for
 
 **Version history (in brief)**:
 
+- 1.10.0 Introduce a "letters" matcher, since "w" matches numbers
+  also.
 - 1.9.1 Fix deprecation warnings around backslashes in regex strings
   (thanks Mickaël Schoentgen). Also fix some documentation formatting
   issues.
@@ -403,5 +406,5 @@ the pattern, the actual match represents the shortest successful match for
   and removed the restriction on mixing fixed-position and named fields
 - 1.0.0 initial release
 
-This code is copyright 2012-2017 Richard Jones <richard@python.org>
+This code is copyright 2012-2019 Richard Jones <richard@python.org>
 See the end of the source file for the license of use.
diff --git a/parse.py b/parse.py
index 3aa4ca4..9da2528 100644
--- a/parse.py
+++ b/parse.py
@@ -133,8 +133,9 @@ The differences between `parse()` and `format()` are:
 ===== =========================================== ========
 Type  Characters Matched                          Output
 ===== =========================================== ========
-w     Letters and underscore                      str
-W     Non-letter and underscore                   str
+l     Letters (ASCII)                             str
+w     Letters, numbers and underscore             str
+W     Not letters, numbers and underscore         str
 s     Whitespace                                  str
 S     Non-whitespace                              str
 d     Digits (effectively integer numbers)        int
@@ -343,6 +344,8 @@ the pattern, the actual match represents the shortest successful match for
 
 **Version history (in brief)**:
 
+- 1.10.0 Introduce a "letters" matcher, since "w" matches numbers
+  also.
 - 1.9.1 Fix deprecation warnings around backslashes in regex strings
   (thanks Mickaël Schoentgen). Also fix some documentation formatting
   issues.
@@ -404,12 +407,12 @@ the pattern, the actual match represents the shortest successful match for
   and removed the restriction on mixing fixed-position and named fields
 - 1.0.0 initial release
 
-This code is copyright 2012-2017 Richard Jones <richard@python.org>
+This code is copyright 2012-2019 Richard Jones <richard@python.org>
 See the end of the source file for the license of use.
 '''
 
 from __future__ import absolute_import
-__version__ = '1.9.1'
+__version__ = '1.10.0'
 
 # yes, I now have two problems
 import re
@@ -643,7 +646,7 @@ class RepeatedNameError(ValueError):
 REGEX_SAFETY = re.compile(r'([?\\\\.[\]()*+\^$!\|])')
 
 # allowed field types
-ALLOWED_TYPES = set(list('nbox%fFegwWdDsS') +
+ALLOWED_TYPES = set(list('nbox%fFegwWdDsSl') +
     ['t' + c for c in 'ieahgcts'])
 
 
@@ -1059,7 +1062,8 @@ class Parser(object):
             self._type_conversions[group] = partial(date_convert, mm=n+1, dd=n+3,
                 hms=n + 5)
             self._group_index += 5
-
+        elif type == 'l':
+            s = r'[A-Za-z]+'
         elif type:
             s = r'\%s+' % type
         elif format.get('precision'):
@@ -1299,7 +1303,7 @@ def compile(format, extra_types=None, case_sensitive=False):
     return Parser(format, extra_types=extra_types)
 
 
-# Copyright (c) 2012-2013 Richard Jones <richard@python.org>
+# Copyright (c) 2012-2019 Richard Jones <richard@python.org>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
diff --git a/setup.py b/setup.py
index 6fc2090..60136cb 100755
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ setup(
     description = "parse() is the opposite of format()",
     long_description = __doc__,
     author = "Richard Jones",
-    author_email = "rjones@ekit-inc.com",
+    author_email = "richard@python.org",
     py_modules = ['parse'],
     url = 'https://github.com/r1chardj0n3s/parse',
     classifiers = [


Test patch:
diff --git a/test_parse.py b/test_parse.py
index 8e09a39..f84c05e 100755
--- a/test_parse.py
+++ b/test_parse.py
@@ -663,6 +663,16 @@ class TestParse(unittest.TestCase):
             p = parse.compile('{:ti}' * 15)
             self.assertRaises(parse.TooManyFields, p.parse, '')
 
+    def test_letters(self):
+        res = parse.parse('{:l}', '')
+        self.assertIsNone(res)
+        res = parse.parse('{:l}', 'sPaM')
+        self.assertEqual(res.fixed, ('sPaM', ))
+        res = parse.parse('{:l}', 'sP4M')
+        self.assertIsNone(res)
+        res = parse.parse('{:l}', 'sP_M')
+        self.assertIsNone(res)
+
 
 class TestSearch(unittest.TestCase):
     def test_basic(self):
@@ -686,7 +696,6 @@ class TestSearch(unittest.TestCase):
         self.assertEqual(r.fixed, (42,))
 
 
-
 class TestFindall(unittest.TestCase):
     def test_findall(self):
         # basic findall() test