Skip to content

Commit b956107

Browse files
committed
Add full command line tests
* minimal tests for attrib, inventory and gen * update the code accordingly Signed-off-by: Philippe Ombredanne <[email protected]>
1 parent c5ac448 commit b956107

File tree

14 files changed

+294
-129
lines changed

14 files changed

+294
-129
lines changed

setup.cfg

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,23 @@ release = clean --all sdist --formats=bztar,zip bdist_wheel
1010
[tool:pytest]
1111
norecursedirs =
1212
.git
13+
.cache
14+
.settings
1315
bin
1416
dist
17+
dist
1518
build
1619
_build
17-
dist
18-
local
19-
ci
2020
docs
2121
man
2222
share
23-
samples
24-
.cache
25-
.settings
23+
example
2624
etc
2725
Include
2826
include
2927
Lib
3028
lib
29+
local
3130
Scripts
3231
thirdparty
3332
tmp

src/attributecode/attrib.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
from __future__ import print_function
1919
from __future__ import unicode_literals
2020

21-
import codecs
2221
import collections
2322
import datetime
23+
import io
2424
import os
2525

2626
import jinja2
@@ -159,8 +159,9 @@ def generate_from_file(abouts, template_loc=DEFAULT_TEMPLATE_FILE, variables=Non
159159
Return a tuple of (error, attribution text) where error is an Error object
160160
or None and attribution text is the generated text or None.
161161
"""
162+
162163
template_loc = add_unc(template_loc)
163-
with codecs.open(template_loc, 'rb', encoding='utf-8') as tplf:
164+
with io.open(template_loc, encoding='utf-8') as tplf:
164165
tpls = tplf.read()
165166
return generate(abouts, template=tpls, variables=variables)
166167

@@ -267,9 +268,9 @@ def generate_and_save(abouts, output_location, template_loc=None, variables=None
267268
if rendering_error:
268269
errors.append(rendering_error)
269270

270-
if rendered:
271+
if rendered:
271272
output_location = add_unc(output_location)
272-
with codecs.open(output_location, 'wb', encoding='utf-8') as of:
273+
with io.open(output_location, 'w', encoding='utf-8') as of:
273274
of.write(rendered)
274275

275276
return errors

src/attributecode/cmd.py

Lines changed: 62 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020

2121
from collections import defaultdict
2222
import errno
23+
import io
2324
import logging
2425
import os
2526
import sys
2627

2728
import click
28-
from attributecode.attrib import check_template
29-
import codecs
3029
# silence unicode literals warnings
3130
click.disable_unicode_literals_warning = True
3231

@@ -37,6 +36,8 @@
3736
from attributecode import __version__
3837
from attributecode import DEFAULT_MAPPING
3938
from attributecode import severities
39+
from attributecode.attrib import check_template
40+
from attributecode.attrib import DEFAULT_TEMPLATE_FILE
4041
from attributecode.attrib import generate_and_save as generate_attribution_doc
4142
from attributecode.gen import generate as generate_about_files
4243
from attributecode.model import collect_inventory
@@ -221,8 +222,8 @@ def inventory(location, output, mapping, mapping_file,
221222

222223
errors_count = report_errors(errors, quiet, verbose, log_file_loc=output + '-error.log')
223224
if not quiet:
224-
msg = 'Inventory collected with {severe_error_count} errors or warnings detected.'
225-
click.echo(msg.format(**locals()))
225+
msg = 'Inventory collected in {output}.'.format(**locals())
226+
click.echo(msg)
226227
sys.exit(errors_count)
227228

228229

@@ -235,11 +236,11 @@ def inventory(location, output, mapping, mapping_file,
235236

236237
@click.argument('location',
237238
required=True,
238-
type=click.Path(exists=True, file_okay=True, readable=True, resolve_path=True))
239+
type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True))
239240

240241
@click.argument('output',
241242
required=True,
242-
type=click.Path(exists=False, writable=True, dir_okay=False, resolve_path=True))
243+
type=click.Path(exists=True, writable=True, file_okay=False, dir_okay=True, resolve_path=True))
243244

244245
# FIXME: the CLI UX should be improved with two separate options for API key and URL
245246
@click.option('--fetch-license',
@@ -312,12 +313,12 @@ def gen(location, output,
312313
fetch_license=fetch_license,
313314
mapping_file=mapping_file
314315
)
315-
abouts_count = len(abouts)
316316

317317
errors_count = report_errors(errors, quiet, verbose, log_file_loc=output + '-error.log')
318318
if not quiet:
319-
msg = '{abouts_count} .ABOUT files generated with {errors_count} errors/warnings.'
320-
click.echo(msg.format(**locals()))
319+
abouts_count = len(abouts)
320+
msg = '{abouts_count} .ABOUT files generated in {output}.'.format(**locals())
321+
click.echo(msg)
321322
sys.exit(errors_count)
322323

323324

@@ -350,7 +351,7 @@ def validate_variables(ctx, param, value):
350351

351352
@click.argument('output',
352353
required=True,
353-
type=click.Path(exists=False, writable=True, resolve_path=True))
354+
type=click.Path(exists=False, writable=True, dir_okay=False, resolve_path=True))
354355

355356
@click.option('--template',
356357
metavar='TEMPLATE_FILE_PATH',
@@ -364,7 +365,7 @@ def validate_variables(ctx, param, value):
364365
help='Add variable(s) as key=value for use in a custom attribution template.')
365366

366367
@click.option('--inventory',
367-
type=click.Path(exists=True, file_okay=True, resolve_path=True),
368+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
368369
help='Path to an optional JSON or CSV inventory file listing the '
369370
'subset of .ABOUT files paths to consider when generating the attribution document.')
370371

@@ -407,13 +408,16 @@ def attrib(location, output, template, variable,
407408
if mapping:
408409
mapping_file = DEFAULT_MAPPING
409410

410-
# Check for template early
411-
with codecs.open(template, 'rb', encoding='utf-8') as templatef:
412-
template_error = check_template(templatef.read())
413-
if template_error:
414-
lineno, message = template_error
415-
raise click.UsageError(
416-
'Template validation error at line: {lineno}: "{message}"'.format(**locals()))
411+
# Check template syntax early
412+
if template:
413+
with io.open(template, encoding='utf-8') as templatef:
414+
template_error = check_template(templatef.read())
415+
if template_error:
416+
lineno, message = template_error
417+
raise click.UsageError(
418+
'Template validation error at line: {lineno}: "{message}"'.format(**locals()))
419+
else:
420+
template = DEFAULT_TEMPLATE_FILE
417421

418422
# accept zipped ABOUT files as input
419423
if location.lower().endswith('.zip'):
@@ -434,8 +438,8 @@ def attrib(location, output, template, variable,
434438
errors_count = report_errors(errors, quiet, verbose, log_file_loc=output + '-error.log')
435439

436440
if not quiet:
437-
msg = 'Attribution generated with {errors_count} errors/warnings.'
438-
click.echo(msg.format(**locals()))
441+
msg = 'Attribution generated in: {output}'.format(**locals())
442+
click.echo(msg)
439443
sys.exit(errors_count)
440444

441445

@@ -444,7 +448,8 @@ def attrib(location, output, template, variable,
444448
######################################################################
445449

446450
@cli.command(cls=AboutCommand,
447-
short_help='Validate that the format of .ABOUT files is correct.')
451+
short_help='Validate that the format of .ABOUT files is correct and report '
452+
'errors and warnings.')
448453

449454
@click.argument('location',
450455
required=True,
@@ -465,17 +470,7 @@ def check(location, verbose):
465470
print_version()
466471
click.echo('Checking ABOUT files...')
467472
errors, _abouts = collect_inventory(location)
468-
if verbose:
469-
reportable = errors
470-
else:
471-
reportable = filter_errors(errors, minimum_severity=WARNING)
472-
473-
severe_errors_count = report_errors(reportable, quiet=False, verbose=True)
474-
475-
if severe_errors_count:
476-
click.echo('Found {severe_error_count} errors or warnings.'.format(severe_errors_count))
477-
else:
478-
click.echo('No error found.')
473+
severe_errors_count = report_errors(errors, quiet=False, verbose=verbose)
479474
sys.exit(severe_errors_count)
480475

481476

@@ -485,56 +480,49 @@ def check(location, verbose):
485480

486481
def report_errors(errors, quiet, verbose, log_file_loc=None):
487482
"""
488-
Return a number of severe errors and display the `errors` list of Error
489-
objects based on the `quiet` and `verbose` flags.
483+
Report the `errors` list of Error objects to screen based on the `quiet` and
484+
`verbose` flags.
490485
491-
If `log_file_loc` directory is provided also write the log to a
492-
file as this location if severe errors are detected.
486+
If `log_file_loc` file location is provided also write a verbose log to this
487+
file.
488+
Return True if there were severe error reported.
493489
"""
494490
errors = unique(errors)
491+
messages, severe_errors_count = get_error_messages(errors, quiet, verbose)
492+
for msg in messages:
493+
click.echo(msg)
494+
if log_file_loc:
495+
log_msgs, _ = get_error_messages(errors, quiet=False, verbose=True)
496+
with io.open(log_file_loc, 'w', encoding='utf-8') as lf:
497+
lf.write('\n'.join(log_msgs))
498+
return severe_errors_count
495499

496-
logger = logging.getLogger(__name__)
497-
handler = logging.StreamHandler()
498-
handler.setLevel(logging.CRITICAL)
499-
handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
500-
logger.addHandler(handler)
501-
502-
file_logger = logging.getLogger(__name__ + '_file')
503500

501+
def get_error_messages(errors, quiet=False, verbose=False):
502+
"""
503+
Return a tuple of (list of error message strings to report,
504+
severe_errors_count) given an `errors` list of Error objects and using the
505+
`quiet` and `verbose` flags.
506+
"""
507+
errors = unique(errors)
504508
severe_errors = filter_errors(errors, WARNING)
505509
severe_errors_count = len(severe_errors)
506510

507-
log_file_obj = None
508-
try:
509-
# Create error.log if problematic_error detected
510-
if severe_errors and log_file_loc:
511-
# FIXME: this
512-
log_file = open(log_file_loc, 'w')
513-
error_msg = '{} errors or warnings detected.'.format(severe_errors_count)
514-
log_file.write(error_msg)
515-
file_handler = logging.FileHandler(log_file)
516-
file_logger.addHandler(file_handler)
517-
518-
519-
for severity, message in errors:
520-
sevcode = severities.get(severity) or 'UNKNOWN'
521-
msg = '{sevcode}: {message}'.format(**locals())
522-
if not quiet:
523-
if verbose:
524-
click.echo(msg)
525-
elif severity >= WARNING:
526-
click.echo(msg)
527-
if log_file_loc:
528-
# The logger will only log error for severity >= 30
529-
file_logger.log(severity, msg)
530-
531-
finally:
532-
if log_file_obj:
533-
try:
534-
log_file_obj.close()
535-
except:
536-
pass
537-
return severe_errors_count
511+
messages = []
512+
513+
if severe_errors and not quiet:
514+
error_msg = 'Command completed with {} errors or warnings.'.format(severe_errors_count)
515+
messages.append(error_msg)
516+
517+
for severity, message in errors:
518+
sevcode = severities.get(severity) or 'UNKNOWN'
519+
msg = '{sevcode}: {message}'.format(**locals())
520+
if not quiet:
521+
if verbose:
522+
messages .append(msg)
523+
elif severity >= WARNING:
524+
messages .append(msg)
525+
return messages, severe_errors_count
538526

539527

540528
def filter_errors(errors, minimum_severity=WARNING):

0 commit comments

Comments
 (0)