Skip to content

Commit 5e15cfd

Browse files
Update fpclib wiki to 1.6
1 parent eb56b0c commit 5e15cfd

File tree

6 files changed

+173
-16
lines changed

6 files changed

+173
-16
lines changed

build.bat

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
@echo off
2-
if exist dist del /q dist build __pycache__
3-
if exist doc\build del /q "doc\build"
2+
del /s /q dist build __pycache__
3+
del /s /q "doc\build"
4+
rmdir /s /q dist build __pycache__
5+
rmdir /s /q "doc\build"
46
py metadata.py
57
echo.
68
echo ---------- Building Documentation ----------

doc/replacer.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import fpclib
2+
import os
3+
4+
fpclib.replace(fpclib.scan_dir("build"), "_sources", "sources")
5+
fpclib.replace(fpclib.scan_dir("build"), "_static", "static")
6+
7+
os.chdir("build/html")
8+
os.rename("_sources", "sources")
9+
os.rename("_static", "static")

doc/source/classes.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ Curation
1010

1111
.. autoclass:: Curation
1212

13-
.. autofunction:: __init__
14-
1513
Attributes
1614
----------
1715

@@ -159,3 +157,16 @@ BrokenCuration
159157
==============
160158

161159
.. autoclass:: BrokenCuration
160+
161+
DateParser
162+
==========
163+
164+
.. autoclass:: DateParser
165+
166+
Methods
167+
-------
168+
169+
:hide:`parse()`
170+
^^^^^^^^^^^^^^^
171+
172+
.. automethod:: DateParser.parse

doc/source/globals.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,21 @@ Other
208208

209209
.. autodata:: MONTHS
210210

211+
:hide:`DP_US`
212+
^^^^^^^^^^^^^
213+
214+
.. autodata:: DP_US
215+
216+
:hide:`DP_UK`
217+
^^^^^^^^^^^^^
218+
219+
.. autodata:: DP_UK
220+
221+
:hide:`DP_ISO`
222+
^^^^^^^^^^^^^^
223+
224+
.. autodata:: DP_ISO
225+
211226
Other
212227
=====
213228

fpclib/__init__.py

Lines changed: 131 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import re
1212
import uuid
1313
import pickle
14-
import traceback
1514
import stat
1615
import shutil
1716

@@ -258,6 +257,30 @@
258257
"""
259258

260259
MONTHS = {
260+
"JANUARY": "01",
261+
"FEBRUARY": "02",
262+
"MARCH": "03",
263+
"APRIL": "04",
264+
"MAY": "05",
265+
"JUNE": "06",
266+
"JULY": "07",
267+
"AUGUST": "08",
268+
"SEPTEMBER": "09",
269+
"OCTOBER": "10",
270+
"NOVEMBER": "11",
271+
"DECEMBER": "12",
272+
"JAN": "01",
273+
"FEB": "02",
274+
"MAR": "03",
275+
"APR": "04",
276+
"MAY": "05",
277+
"JUN": "06",
278+
"JUL": "07",
279+
"AUG": "08",
280+
"SEP": "09",
281+
"OCT": "10",
282+
"NOV": "11",
283+
"DEC": "12",
261284
"Jan": "01",
262285
"Feb": "02",
263286
"Mar": "03",
@@ -271,7 +294,7 @@
271294
"Nov": "11",
272295
"Dec": "12"
273296
}
274-
"""A dictionary mapping three letter month codes to 2 number month codes."""
297+
"""A dictionary mapping uppercase month names to 2 number month codes."""
275298

276299
DEBUG_LEVEL = 1
277300
"""A global value that determines what debug information gets printed. This applies to the whole module, and you can modify it when you want to. Possible values:
@@ -1222,7 +1245,7 @@ def curate(items, curation_class, use_title=False, save=False, ignore_errs=False
12221245
debug('Curating index {}, "{}"', 1, i, item)
12231246
TABULATION += 1
12241247
try:
1225-
curation_class(url=item, **data).save(use_title, overwrite, validate=validate)
1248+
curation_class(url=item, **data).save(use_title, overwrite, True, validate=validate)
12261249
except InvalidMetadataError as e:
12271250
debug('Skipping curation, {}', 1, str(e), pre='[WARN] ')
12281251
errs.append((item, e, data))
@@ -1330,7 +1353,7 @@ def curate_regex(items, links, use_title=False, save=False, ignore_errs=False, o
13301353
debug('Curating index {}, "{}", with class "{}"', 1, i, item, link[1].__name__)
13311354
TABULATION += 1
13321355
try:
1333-
link[1](url=item, **data).save(use_title, overwrite, validate=validate)
1356+
link[1](url=item, **data).save(use_title, overwrite, True, validate=validate)
13341357
except InvalidMetadataError as e:
13351358
debug('Skipping curation, {}', 1, str(e), pre='[WARN] ')
13361359
errs.append((item, e, data))
@@ -1391,7 +1414,12 @@ def load(curation):
13911414
return c
13921415

13931416
class Curation:
1394-
"""This is the base class for every kind of curation. If you want a good tutorial on how to use this class, see :doc:`The Basics </basics>`. Extend this class to redefine it's methods."""
1417+
"""This is the base class for every kind of curation. If you want a good tutorial on how to use this class, see :doc:`The Basics </basics>`. Extend this class to redefine it's methods. Constructor:
1418+
1419+
Accepts a single :class:`Curation` object as an argument or arguments in the same format as :func:`Curation.set_meta()`. The new curation will first have it's metadata, logo, screenshot, added args, and id deep-copied from :code:`curation`'s first if it's available, then have that data modified with :code:`kwargs` if available. This curation object will be linked to that curation object.
1420+
1421+
:raises TypeError: If :code:`curation` is not an instance of :class:`Curation`.
1422+
"""
13951423

13961424
RESERVED_APPS = {'extras', 'message'}
13971425
"""A set containing all of the reserved headings that cannot be used in additional applications. The check is case-insensitive, hence they are lowercase."""
@@ -1476,10 +1504,6 @@ class Curation:
14761504
"""
14771505

14781506
def __init__(self, curation=None, **kwargs):
1479-
"""Accepts a single :class:`Curation` object as an argument or arguments in the same format as :func:`Curation.set_meta()`. The new curation will first have it's metadata, logo, screenshot, added args, and id deep-copied from :code:`curation`'s first if it's available, then have that data modified with :code:`kwargs` if available. This curation object will be linked to that curation object.
1480-
1481-
:raises TypeError: If :code:`curation` is not an instance of :class:`Curation`.
1482-
"""
14831507
if not curation:
14841508
self.meta = {
14851509
'Title': None,
@@ -1692,12 +1716,12 @@ def save_image(self, url, file_name):
16921716
"""
16931717
download_image(url, name=file_name)
16941718

1695-
def save(self, use_title=False, overwrite=False, parse=True, validate=0, save_items=EVERYTHING):
1719+
def save(self, use_title=False, overwrite=False, parse=False, validate=0, save_items=EVERYTHING):
16961720
"""Save the curation to a folder with the name of :attr:`Curation.id`. Consecutive calls to this method will not overwrite the previous folder, but will instead save it as "Curation (2)", "Curation (3)", etc.
16971721
16981722
:param str use_title: If True, the folder will be generated with the title of the curation instead of its id.
16991723
:param bool overwrite: If True, this method will mix and overwrite files in existing curation folders instead of making the folder "Curation (2)", "Curation (3)", etc.
1700-
:param bool parse: *Added in 1.3*: If False, this function will not call :func:`Curation.parse()`.
1724+
:param bool parse: *Added in 1.3*: If True, this function will call :func:`Curation.parse()` before saving metadata.
17011725
:param int validate: *Added in 1.3*: Mode to validate this curation's metadata with. 0 (default) means do not validate, 1 means flexibly validate, and 2 means rigidly validate.
17021726
:param int save_items: Flags determining what items to save as part of this curation. By default this is :data:`EVERYTHING`. If you wanted to save only the meta and logo, for example, use :code:`save_items=META|LOGO`.
17031727
@@ -1993,6 +2017,102 @@ def get_errors(self, rigid=True):
19932017
finally:
19942018
TABULATION -= 1
19952019
return errs
2020+
2021+
class DateParser:
2022+
"""Initialize a regex-powered date parser that gets initialized with a specific format and can parse any date into the proper iso format. It does not check that the date is a real date. The constructor takes a string :code:`format` specifying a regex to search for in future parsed strings. Note that the regex is case insensitive. Use these macros in the format string to specify parts of the date:
2023+
2024+
"<y>" for year number to match - replaced with the capture group "(?P<year>\d{4})",
2025+
"<m>" for month to match - replaced with the capture group "(?P<month>\d{1,3}|[A-Za-z]+)", and
2026+
"<d>" for day to match - replaced with the capture group "(?P<day>\d{1,3})"
2027+
2028+
Month and day are optional, though using day requires using month. Note that the year, month, and day are automatically padded to the right number of zeros (4, 2, 2) automatically.
2029+
2030+
If the macros don't quite work for you, feel free to use named capture groups and the callbacks "year", "month", and "day", which are called on the respective matched parts of a parsed string to turn it into the right number format to use in the returned string. If the "month" callback is not set, it defaults to :func:`DateParser.get_month`.
2031+
2032+
:param str format: A string containing a regex and macros specifying how to parse strings.
2033+
:param func year: A function to turn the matched "year" part of a parsed date into the right number format.
2034+
:param func month: A function to turn the matched "month" part of a parsed date into the right number format.
2035+
:param func day: A function to turn the matched "day" part of a parsed date into the right number format.
2036+
2037+
:raises ValueError: if the given format does not contain a "year" named group/macro or contains a "day" named group/macro without the "month" named group/macro.
2038+
2039+
:since 1.6:
2040+
"""
2041+
2042+
def __init__(self, format, year=None, month=None, day=None):
2043+
text = format.\
2044+
replace("<y>", r"(?P<year>\d{4})").\
2045+
replace("<m>", r"(?P<month>\d{1,3}|[A-Za-z]+)").\
2046+
replace("<d>", r"(?P<day>\d{1,3})")
2047+
2048+
if "(?P<year>" not in text or ("(?P<month>" not in text and "(?P<day>" in text):
2049+
raise ValueError("The given format is invalid")
2050+
2051+
self.format = re.compile(text, re.I)
2052+
self.year = year
2053+
self.month = month or DateParser.get_month
2054+
self.day = day
2055+
2056+
def get_month(s):
2057+
"""The default method called to turn the matched "month" part of a parsed date into the right number format. It tries to to find the first three characters of the string put into uppercase in :data:`fpclib.MONTHS`, then just returns :code:`s` if it fails."""
2058+
if len(s) < 3: return s
2059+
try:
2060+
return MONTHS[s[:3].upper()]
2061+
except KeyError:
2062+
return s
2063+
2064+
def parse(self, s):
2065+
"""Uses this date format object to parse the given string :code:`s` into a proper iso date.
2066+
2067+
:param str s: A string to parse for a date.
2068+
2069+
:returns: An iso date parsed from string :code:`s`
2070+
2071+
:raises ValueError: if no date in :code:`s` could be found.
2072+
"""
2073+
match = self.format.search(s)
2074+
if not match: raise ValueError(f'No date in "{s}"')
2075+
2076+
y = match["year"]
2077+
if not y: raise ValueError(f'No year in "{s}"')
2078+
try: m = match["month"]
2079+
except IndexError: m = None
2080+
try: d = match["day"]
2081+
except IndexError: d = None
2082+
2083+
if d and not m: raise ValueError(f'Day but no month in "{s}"')
2084+
2085+
if self.year:
2086+
year = self.year(y).zfill(4)
2087+
else:
2088+
year = y.zfill(4)
2089+
2090+
if m:
2091+
if self.month:
2092+
month = "-" + self.month(m).zfill(2)
2093+
else:
2094+
month = "-" + m.zfill(2)
2095+
else:
2096+
month = ""
2097+
2098+
if d:
2099+
if self.day:
2100+
day = "-" + self.day(d).zfill(2)
2101+
else:
2102+
day = "-" + d.zfill(2)
2103+
else:
2104+
day = ""
2105+
2106+
return year + month + day
2107+
2108+
DP_US = DateParser(r"<m>(\s*.??<d>\w*)?,?\s*.??<y>")
2109+
"""A :class:`DateParser` that parses dates in the american format of "March 5th, 2016", "3/5/2016", "March 2016" or similar."""
2110+
DP_UK = DateParser(r"(<d>\w*(\s*of)?\s*.??)?<m>,?\s*.??<y>")
2111+
"""A :class:`DateParser` that parses dates in the european format of "5th of March, 2016", "5/3/2016", "March 2016" or similar."""
2112+
DP_ISO = DateParser(r"<y>(\s*.??<m>)?(\s*.??<d>\w*)?")
2113+
"""A :class:`DateParser` that parses dates in the format of "2016 March 5th" or similar."""
2114+
2115+
19962116
if __name__ == '__main__':
19972117
test()
19982118

metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def get_build():
1414
file.write(str(build))
1515

1616
NAME = 'fpclib'
17-
RELEASE = '1.5.2.' + get_build()
17+
RELEASE = '1.6.0.' + get_build()
1818
VERSION = re.match('\d+\.\d+', RELEASE).group(0)
1919
AUTHOR = 'mathgeniuszach'
2020

0 commit comments

Comments
 (0)