Skip to content

Commit 5ffb028

Browse files
committed
🐛 Fix SQLite convert_date converter
1 parent f6919ab commit 5ffb028

File tree

10 files changed

+45
-10
lines changed

10 files changed

+45
-10
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ This command is only available on v1.3.2 and greater. Otherwise, please provide
3030

3131
**Additional context**
3232
Add any other context about the problem here.
33+
34+
In case of errors please run the same command with `--debug`. This option is only available on v1.4.12 or greater.

mysql_to_sqlite3/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__title__ = "mysql-to-sqlite3"
33
__description__ = "A simple Python tool to transfer data from MySQL to SQLite 3"
44
__url__ = "https://github.com/techouse/mysql-to-sqlite3"
5-
__version__ = "1.4.11"
5+
__version__ = "1.4.12"
66
__author__ = "Klemen Tusar"
77
__author_email__ = "techouse@gmail.com"
88
__license__ = "MIT"

mysql_to_sqlite3/cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"result sets, need to be combined or computed with each other.",
113113
)
114114
@click.option("-q", "--quiet", is_flag=True, help="Quiet. Display only errors.")
115+
@click.option("--debug", is_flag=True, help="Debug mode. Will throw exceptions.")
115116
@click.version_option(
116117
message=tabulate(info(), headers=["software", "version"], tablefmt="github")
117118
)
@@ -136,6 +137,7 @@ def cli(
136137
vacuum,
137138
use_buffered_cursors,
138139
quiet,
140+
debug,
139141
):
140142
"""Transfer MySQL to SQLite using the provided CLI options."""
141143
try:
@@ -163,8 +165,12 @@ def cli(
163165
)
164166
converter.transfer()
165167
except KeyboardInterrupt:
168+
if debug:
169+
raise
166170
print("\nProcess interrupted. Exiting...")
167171
sys.exit(1)
168172
except Exception as err: # pylint: disable=W0703
173+
if debug:
174+
raise
169175
print(err)
170176
sys.exit(1)

mysql_to_sqlite3/sqlite_utils.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
from __future__ import division
44

55
import sqlite3
6-
from datetime import timedelta
6+
from datetime import date, datetime, timedelta
77
from decimal import Decimal
8+
from sys import version_info
89

10+
import six
911
from pytimeparse.timeparse import timeparse
1012

13+
if version_info.major == 3 and 4 <= version_info.minor <= 6:
14+
from backports.datetime_fromisoformat import MonkeyPatch # pylint: disable=E0401
15+
16+
MonkeyPatch.patch_fromisoformat()
17+
1118

1219
def adapt_decimal(value):
1320
"""Convert decimal.Decimal to string."""
@@ -45,3 +52,20 @@ class CollatingSequences:
4552
BINARY = "BINARY"
4653
NOCASE = "NOCASE"
4754
RTRIM = "RTRIM"
55+
56+
57+
def convert_date(value):
58+
"""Handle SQLite date conversion."""
59+
if six.PY3:
60+
try:
61+
return date.fromisoformat(value.decode())
62+
except ValueError as err:
63+
raise ValueError( # pylint: disable=W0707
64+
"DATE field contains {}".format(err)
65+
)
66+
try:
67+
return datetime.strptime(value.decode(), "%Y-%m-%d").date()
68+
except ValueError as err:
69+
raise ValueError( # pylint: disable=W0707
70+
"DATE field contains Invalid isoformat string: {}".format(err)
71+
)

mysql_to_sqlite3/transporter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
CollatingSequences,
2121
adapt_decimal,
2222
adapt_timedelta,
23+
convert_date,
2324
convert_decimal,
2425
convert_timedelta,
2526
encode_data_for_sqlite,
@@ -102,6 +103,7 @@ def __init__(self, **kwargs):
102103
sqlite3.register_adapter(Decimal, adapt_decimal)
103104
sqlite3.register_converter("DECIMAL", convert_decimal)
104105
sqlite3.register_adapter(timedelta, adapt_timedelta)
106+
sqlite3.register_converter("DATE", convert_date)
105107
sqlite3.register_converter("TIME", convert_timedelta)
106108

107109
self._sqlite = sqlite3.connect(

requirements_dev.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ tox
2323
tqdm>=4.35.0
2424
packaging>=20.3
2525
tabulate<0.8.6 ; python_version<"3.5"
26-
tabulate ; python_version>="3.5"
26+
tabulate ; python_version>="3.5"
27+
backports-datetime-fromisoformat>=1.0.0 ; python_version>="3.4" and python_version<="3.6"

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"tqdm>=4.35.0",
2121
"tabulate<0.8.6 ; python_version<'3.5'",
2222
"tabulate ; python_version>='3.5'",
23+
"backports-datetime-fromisoformat>=1.0.0 ; python_version>='3.4' and python_version<='3.6'",
2324
]
2425

2526
about = {}

tests/conftest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def mysql_credentials(pytestconfig):
183183
port = pytestconfig.getoption("mysql_port") or 3306
184184
if pytestconfig.getoption("use_docker"):
185185
while is_port_in_use(port, pytestconfig.getoption("mysql_host")):
186-
if port >= 2 ** 16 - 1:
186+
if port >= 2**16 - 1:
187187
pytest.fail(
188188
"No ports appear to be available on the host {}".format(
189189
pytestconfig.getoption("mysql_host")
@@ -342,7 +342,6 @@ def mysql_database(tmpdir_factory, mysql_instance, mysql_credentials, faker):
342342
if database_exists(db.engine.url):
343343
drop_database(db.engine.url)
344344

345-
346345
else:
347346

348347
@pytest.fixture(scope="session")

tests/factories.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,23 @@ class MiscFactory(factory.Factory):
3131
class Meta:
3232
model = models.Misc
3333

34-
big_integer_field = factory.Faker("pyint", max_value=10 ** 9)
34+
big_integer_field = factory.Faker("pyint", max_value=10**9)
3535
large_binary_field = factory.Faker("binary", length=1024 * 10)
3636
boolean_field = factory.Faker("boolean")
3737
char_field = factory.Faker("text", max_nb_chars=255)
3838
date_field = factory.Faker("date_this_decade")
3939
date_time_field = factory.Faker("date_time_this_century")
4040
decimal_field = factory.Faker("pydecimal", left_digits=8, right_digits=2)
4141
float_field = factory.Faker("pyfloat", left_digits=8, right_digits=4)
42-
integer_field = factory.Faker("pyint", min_value=-(2 ** 31), max_value=2 ** 31 - 1)
42+
integer_field = factory.Faker("pyint", min_value=-(2**31), max_value=2**31 - 1)
4343
if environ.get("LEGACY_DB", "0") == "0":
4444
json_field = factory.Faker("pydict")
4545
nchar_field = factory.Faker("text", max_nb_chars=255)
4646
numeric_field = factory.Faker("pyfloat", left_digits=8, right_digits=4)
4747
unicode_field = factory.Faker("text", max_nb_chars=255)
4848
real_field = factory.Faker("pyfloat", left_digits=8, right_digits=4)
4949
small_integer_field = factory.Faker(
50-
"pyint", min_value=-(2 ** 15), max_value=2 ** 15 - 1
50+
"pyint", min_value=-(2**15), max_value=2**15 - 1
5151
)
5252
string_field = factory.Faker("text", max_nb_chars=255)
5353
text_field = factory.Faker("text", max_nb_chars=1024)

tests/func/test_cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ def test_invalid_database_port(
162162
self, cli_runner, sqlite_database, mysql_database, mysql_credentials, faker
163163
):
164164
if six.PY2:
165-
port = choice(xrange(2, 2 ** 16 - 1))
165+
port = choice(xrange(2, 2**16 - 1))
166166
else:
167-
port = choice(range(2, 2 ** 16 - 1))
167+
port = choice(range(2, 2**16 - 1))
168168
if port == mysql_credentials.port:
169169
port -= 1
170170
result = cli_runner.invoke(

0 commit comments

Comments
 (0)