Skip to content

Commit 18de878

Browse files
authored
Merge pull request #152 from DevoInc/test-csv-separarators-quotes
Test csv separarators quotes
2 parents bfa0c69 + eb091bb commit 18de878

24 files changed

+443
-170
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [4.0.2] - 2022-09-01
8+
### Added
9+
* Add a new command-line option for escaping double quotes with `-eq` or `--escape_quotes`.
10+
* Add a check and shows a warning if the file contains double quotes and the option `-eq` is not used.
11+
* Add a new command-line option in tests for testing just one module with `-m <module_name>` or `--module <module_name>`.
712
## [4.0.1] - 2022-08-26
813
### Added
914
* Create GitHub action to publish package in PyPI

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (C) 2018 Devo, Inc.
3+
Copyright (C) 2022 Devo, Inc.
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,50 @@ To launch this script, you need either the environment variables loaded in the s
105105

106106
Its normal, by the way, TCP tests fails in clients or not Devo developers systems.
107107

108-
```bash
108+
```console
109109
~/projects/devo-python-sdk > python setup.py test
110110
```
111111

112-
```bash
112+
```console
113113
~/projects/devo-python-sdk > python run_tests.py
114114
```
115115

116116
You can add option "Coverage" for create HTML report about tests.
117117

118-
```bash
118+
```console
119119
~/projects/devo-python-sdk > python run_tests.py --coverage
120120
```
121121

122+
You can also run the test for just one module. This is a useful feature if you are developing functionality in just one module.
123+
124+
```console
125+
~/projects/devo-python-sdk > python run_tests.py -m SEND_CLI
126+
```
127+
128+
Using the --help flag prints the available modules to use:
129+
130+
```console
131+
~/projects/devo-python-sdk > python run_tests.py --help
132+
usage: run_tests.py [-h] [--coverage [COVERAGE]] [-m [MODULE]]
133+
134+
optional arguments:
135+
-h, --help show this help message and exit
136+
--coverage [COVERAGE]
137+
Generate coverage
138+
-m [MODULE], --module [MODULE]
139+
Run tests for selected module: API_CLI, API_QUERY, API_TASKS, COMMON_CONFIGURATION,
140+
COMMON_DATE_PARSER, SENDER_CLI, SENDER_NUMBER_LOOKUP, SENDER_SEND_DATA, SENDER_SEND_LOOKUP
141+
```
142+
143+
* API_CLI: API Command-line interface tests.
144+
* API_QUERY: Query API tests.
145+
* API_TASKS: Task API tests.
146+
* COMMON_CONFIGURATION: Configuration tests.
147+
* COMMON_DATE_PARSER: Date parser tests.
148+
* SENDER_CLI: Lookup command-line interface tests.
149+
* SENDER_NUMBER_LOOKUP: Numbers in lookup tests
150+
* SENDER_SEND_DATA: Data sending tests.
151+
* SENDER_SEND_LOOKUP: Lookup sending tests.
122152

123153
### Run using Unittest command
124154

devo/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
__description__ = 'Devo Python Library.'
22
__url__ = 'http://www.devo.com'
3-
__version__ = "4.0.1"
3+
__version__ = "4.0.2"
44
__author__ = 'Devo'
55
__author_email__ = 'support@devo.com'
66
__license__ = 'MIT'

devo/common/__init__.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
from .dates.dateparser import parse, parse_string, parse_expression, \
2-
default_from, default_to
3-
from .dates.dateoperations import month, week, day, hour, minute, second, now, \
4-
now_without_ms, today, yesterday, parse_functions
5-
from .dates.dateutils import to_millis, trunc_time, trunc_time_minute, \
6-
test_date_format, get_timestamp
1+
from .dates.dateparser import (
2+
parse, parse_string, parse_expression,
3+
default_from, default_to)
4+
from .dates.dateoperations import (
5+
month, week, day, hour, minute, second, now,
6+
now_without_ms, today, yesterday, parse_functions)
7+
from .dates.dateutils import (
8+
to_millis, trunc_time, trunc_time_minute,
9+
test_date_format, get_timestamp)
710
from .generic.configuration import Configuration
8-
from .logging.log import get_log, set_formatter, get_rotating_file_handler, \
9-
get_stream_handler
11+
from .logging.log import (
12+
get_log, set_formatter, get_rotating_file_handler,
13+
get_stream_handler)
1014
from .loadenv import load_env_file

devo/common/logging/log.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ def set_formatter(msg_format):
3838
return logging.Formatter(msg_format)
3939

4040

41-
def get_rotating_file_handler(path="./",
42-
file_name="history.log",
43-
msg_format='%(asctime)s|%(levelname)s|%(message)s',
44-
max_size=2097152,
45-
backup_count=5,
46-
level=logging.INFO):
41+
def get_rotating_file_handler(
42+
path="./",
43+
file_name="history.log",
44+
msg_format='%(asctime)s|%(levelname)s|%(message)s',
45+
max_size=2097152,
46+
backup_count=5,
47+
level=logging.INFO):
4748
"""Initialize rotating file handler for logger
4849
4950
:return: RotatingFileHandler object

devo/sender/data.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ def __init__(self, config=None, con_type=None,
311311
logging.Handler.__init__(self)
312312
self.logger = logger if logger else \
313313
get_log(handler=get_stream_handler(
314-
msg_format='%(asctime)s|%(levelname)s|Devo-Sender|%(message)s'))
315-
314+
msg_format='%(asctime)s|%(levelname)s|Devo-Sender|%(message)s')
315+
)
316316

317317
self._sender_config = config
318318

@@ -410,8 +410,8 @@ def __connect_ssl(self):
410410
self.reconnection += 1
411411
if self.debug:
412412
self.logger.debug('Conected to %s|%s'
413-
% (repr(self.socket.getpeername())
414-
, str(self.reconnection)))
413+
% (repr(self.socket.getpeername()),
414+
str(self.reconnection)))
415415
self.timestart = int(round(time.time() * 1000))
416416

417417
except socket.error as error:
@@ -520,7 +520,7 @@ def close(self):
520520
if self.socket is not None:
521521
try:
522522
self.socket.shutdown(SHUT_RDWR)
523-
except: # Try else continue
523+
except Exception as e: # Try else continue
524524
pass
525525
self.socket.close()
526526
self.socket = None
@@ -671,7 +671,8 @@ def send_bytes(self, tag, msg, **kwargs):
671671
"""
672672
Send function when bytes, sure py3x. Can be zipped
673673
"""
674-
msg = COMPOSE_BYTES % (self.compose_mem(tag, bytes=True, **kwargs), msg)
674+
msg = COMPOSE_BYTES % (
675+
self.compose_mem(tag, bytes=True, **kwargs), msg)
675676
if kwargs.get('zip', False):
676677
return self.fill_buffer(msg)
677678

devo/sender/lookup.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import time
77

88

9-
109
def find_key_index(value=None, headers=None):
1110
"""Find index of key value in a list"""
1211
for index, val in enumerate(headers):
@@ -143,6 +142,20 @@ def detect_types(reader=None):
143142
types[index] = "str"
144143
return types
145144

145+
def check_quotes(self, file_path) -> bool:
146+
"""
147+
Check if there are quotes in CSV file
148+
:param file_path: path to CSV file
149+
:return: boolean
150+
"""
151+
if self.escape_quotes:
152+
return False
153+
154+
with open(file_path) as f:
155+
line = next((l_temp for l_temp in f if '"' in l_temp), None)
156+
157+
return True if line is not None else False
158+
146159
# Send a whole CSV file
147160
def send_csv(self, path=None, has_header=True, delimiter=',',
148161
quotechar='"', headers=None, key="KEY", historic_tag=None,
@@ -241,7 +254,8 @@ def send_csv(self, path=None, has_header=True, delimiter=',',
241254
except IOError as error:
242255
print("I/O error({0}): {1}".format(error.errno, error.strerror))
243256
except Exception as error:
244-
raise Exception("Unexpected error: %e \n" % error, sys.exc_info()[0])
257+
raise Exception(
258+
"Unexpected error: %e \n" % error, sys.exc_info()[0])
245259

246260
# Basic process
247261
# --------------------------------------------------------------------------

devo/sender/scripts/sender_cli.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ def cli(version):
5050
'"2" (CERT_REQUIRED)', type=int)
5151
@click.option('--check_hostname', help='Verify cert hostname. Default: True',
5252
type=bool)
53-
@click.option('--multiline/--no-multiline', help='Flag for multiline (With '
54-
'break-line in msg). '
55-
'Default False', default=False)
53+
@click.option('--multiline/--no-multiline',
54+
help='Flag for multiline (With '
55+
'break-line in msg). '
56+
'Default False', default=False)
5657
@click.option('--type', help='Connection type: SSL or TCP', default="SSL")
5758
@click.option('--tag', '-t', help='Tag / Table to which the data will be sent '
5859
'in Devo.', default="test.drop.ltsender")
@@ -168,18 +169,31 @@ def data(**kwargs):
168169
default=False)
169170
@click.option('--delimiter', '-d', help='CSV Delimiter char.', default=",")
170171
@click.option('--quotechar', '-qc', help='CSV Quote char.', default='"')
172+
@click.option('--escapequotes', '-eq', is_flag=True,
173+
help='Escape Quotes. Default: False',
174+
default=False)
171175
@click.option('--debug/--no-debug', help='For testing purposes', default=False)
172176
def lookup(**kwargs):
173177
"""Send csv lookups to devo"""
174178
config = configure_lookup(kwargs)
179+
warning_st = 0
180+
181+
# Exit errors by https://tldp.org/LDP/abs/html/exitcodes.html
182+
status_msg = {
183+
64: "Some field contains double quotes in this file."
184+
"If you do not use -eq or --escapequotes it might not work."
185+
}
186+
175187
con = Sender(config=config)
176188

177-
lookup = Lookup(name=config['name'], historic_tag=None, con=con)
189+
lookup = Lookup(name=config['name'], historic_tag=None,
190+
con=con, escape_quotes=config['escapequotes'])
178191

179-
# with open(config['file']) as file:
180-
# line = file.readline()
192+
if lookup.check_quotes(config['file']):
193+
warning_st = 64
181194

182-
lookup.send_csv(config['file'], delimiter=config['delimiter'],
195+
lookup.send_csv(config['file'],
196+
delimiter=config['delimiter'],
183197
quotechar=config['quotechar'],
184198
has_header=True,
185199
# headers=line.rstrip().split(config['delimiter']),
@@ -191,6 +205,10 @@ def lookup(**kwargs):
191205
),
192206
detect_types=config.get("detect_types", False))
193207

208+
if warning_st != 0:
209+
print_warning(status_msg[warning_st])
210+
exit(warning_st)
211+
194212

195213
def configure(args):
196214
""" Configuration of Sender CLI """
@@ -237,3 +255,11 @@ def print_error(error, show_help=False):
237255
click.echo("")
238256
click.echo(click.get_current_context().get_help())
239257
click.echo(click.style(error, fg='red'), err=True)
258+
259+
260+
def print_warning(warning, show_help=False):
261+
"""Class for print warning in shell"""
262+
if show_help:
263+
click.echo("")
264+
click.echo(click.get_current_context().get_help())
265+
click.echo(click.style(warning, fg='yellow'), err=True)

devo/sender/transformsyslog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,4 @@
8080
"NOTICE": SEVERITY_NOTICE,
8181
"INFO": SEVERITY_INFO,
8282
"DEBUG": SEVERITY_DEBUG
83-
}
83+
}

0 commit comments

Comments
 (0)