Skip to content

Commit

Permalink
Merge pull request #1 from raimon49/parse_license_from_classifier
Browse files Browse the repository at this point in the history
Implement 'find_license_from_classifier'
  • Loading branch information
raimon49 authored Feb 7, 2018
2 parents 64f0b64 + c89eb23 commit 08bc90f
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## CHANGELOG

### 1.2.0

* Implement new option `--from-classifier`

### 1.1.0

* Improve document
Expand Down
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

[![Build Status](https://travis-ci.org/raimon49/pip-licenses.svg?branch=master)](https://travis-ci.org/raimon49/pip-licenses) [![PyPI version](https://badge.fury.io/py/pip-licenses.svg)](https://badge.fury.io/py/pip-licenses) [![GitHub Release](https://img.shields.io/github/release/raimon49/pip-licenses.svg)](https://github.com/raimon49/pip-licenses/releases) [![Codecov](https://codecov.io/gh/raimon49/pip-licenses/branch/master/graph/badge.svg)](https://codecov.io/gh/raimon49/pip-licenses) [![BSD License](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/raimon49/pip-licenses/blob/master/LICENSE) [![Requirements Status](https://requires.io/github/raimon49/pip-licenses/requirements.svg?branch=master)](https://requires.io/github/raimon49/pip-licenses/requirements/?branch=master)

Dump the license list of packages installed with pip.
Dump the software license list of Python packages installed with pip.

## Table of Contents

* [Description](#description)
* [Installation](#installation)
* [Usage](#usage)
* [Command\-Line Options](#command-line-options)
* [\-\-from\-classifier](#--from-classifier)
* [\-\-with\-system](#--with-system)
* [\-\-with\-authors](#--with-authors)
* [\-\-with\-urls](#--with-urls)
Expand All @@ -20,7 +21,7 @@ Dump the license list of packages installed with pip.

## Description

`pip-licenses` is a CLI tool for checking the software license of installed packages with pip.
`pip-licenses` is a CLI tool for checking the software license of installed Python packages with pip.

Implemented with the idea inspired by `composer licenses` command in Composer (a.k.a PHP package management tool).

Expand Down Expand Up @@ -51,6 +52,25 @@ Execute the command with your venv (or virtualenv) environment.

## Command-Line Options

### --from-classifier

By default, this tool finds the license from package METADATA or PKG-INFO. However, depending on the type of package, it does not declare a license only in the Classifiers.

(See also): [Set license to MIT in setup.py by alisianoi ・ Pull Request #1058 ・ pypa/setuptools](https://github.com/pypa/setuptools/pull/1058)

If you want to refer to the license declared in Classifiers, use the `--from-classifier` option.

```bash
(venv) $ pip-licenses --from-classifier --with-system
Name Version License
Django 2.0.2 BSD License
PTable 0.9.2 BSD License
pip 9.0.1 MIT License
pip-licenses 1.0.0 MIT License
pytz 2017.3 MIT License
setuptools 38.5.0 MIT License
```

### --with-system

By default, system packages such as pip and setuptools are ignored.
Expand Down Expand Up @@ -81,7 +101,7 @@ When executed with the `--with-authors` option, output with author of the packag

### --with-urls

For packages without METADATA, the license is output as `UNKNOWN`. To get more package information, use the `--with-urls` option.
For packages without METADATA (or PKG-INFO or Classifiers), the license is output as `UNKNOWN`. To get more package information, use the `--with-urls` option.

```bash
(venv) $ pip-licenses --with-urls
Expand Down
24 changes: 22 additions & 2 deletions piplicenses.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@
import sys
import argparse
from email.parser import FeedParser
from email import message_from_string

import pip
from prettytable import PrettyTable

__pkgname__ = 'pip-licenses'
__version__ = '1.1.0'
__version__ = '1.2.0'
__author__ = 'raimon'
__license__ = 'MIT License'
__summary__ = 'Dump the license list of packages installed with pip.'
__summary__ = ('Dump the software license list of '
'Python packages installed with pip.')
__url__ = 'https://github.com/raimon49/pip-licenses'


Expand Down Expand Up @@ -104,6 +106,10 @@ def get_pkg_info(pkg):
for key in METADATA_KEYS:
pkg_info[key] = parsed_metadata.get(key, LICENSE_UNKNOWN)

if args.from_classifier and metadata is not None:
message = message_from_string(metadata)
pkg_info['license'] = find_license_from_classifier(message)

return pkg_info

pkgs = pip.get_installed_distributions()
Expand All @@ -126,6 +132,16 @@ def get_pkg_info(pkg):
return table


def find_license_from_classifier(message):
license_from_classifier = LICENSE_UNKNOWN

for k, v in message.items():
if k == 'Classifier' and v.startswith('License'):
license_from_classifier = v.split(' :: ')[-1]

return license_from_classifier


def get_output_fields(args):
output_fields = list(DEFAULT_OUTPUT_FIELDS)

Expand Down Expand Up @@ -157,6 +173,10 @@ def create_parser():
parser.add_argument('-v', '--version',
action='version',
version='%(prog)s ' + __version__)
parser.add_argument('-c', '--from-classifier',
action='store_true',
default=False,
help='find license from classifier')
parser.add_argument('-s', '--with-system',
action='store_true',
default=False,
Expand Down
50 changes: 48 additions & 2 deletions test_piplicenses.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import (division, print_function,
absolute_import, unicode_literals)
import unittest
from email import message_from_string

from piplicenses import (__pkgname__, create_parser,
get_licenses_table, get_output_fields, get_sortby,
DEFAULT_OUTPUT_FIELDS, SYSTEM_PACKAGES)
find_license_from_classifier,
DEFAULT_OUTPUT_FIELDS, SYSTEM_PACKAGES,
LICENSE_UNKNOWN)


class CommandLineTestCase(unittest.TestCase):
Expand All @@ -19,11 +22,25 @@ def setUp(self):

def _create_pkg_name_columns(self, table):
import copy
index = DEFAULT_OUTPUT_FIELDS.index('Name')

# XXX: access to private API
rows = copy.deepcopy(table._rows)
pkg_name_columns = []
for row in rows:
pkg_name_columns.append(row[index])

return pkg_name_columns

def _create_license_columns(self, table):
import copy
index = DEFAULT_OUTPUT_FIELDS.index('License')

# XXX: access to private API
rows = copy.deepcopy(table._rows)
pkg_name_columns = []
for row in rows:
pkg_name_columns.append(row[0])
pkg_name_columns.append(row[index])

return pkg_name_columns

Expand Down Expand Up @@ -106,5 +123,34 @@ def test_order_url_no_effect(self):
sortby = get_sortby(args)
self.assertEquals('Name', sortby)

def test_from_classifier(self):
from_classifier_args = ['--from-classifier']
args = self.parser.parse_args(from_classifier_args)
table = get_licenses_table(args)

output_fields = get_output_fields(args)
self.assertIn('License', output_fields)

license_columns = self._create_license_columns(table)
license_notation_as_classifier = 'MIT License'
self.assertIn(license_notation_as_classifier, license_columns)

def test_find_license_from_classifier(self):
metadata = ('Metadata-Version: 2.0\r\n'
'Name: pip-licenses\r\n'
'Version: 1.0.0\r\n'
'Classifier: License :: OSI Approved :: MIT License\r\n')
message = message_from_string(metadata)
self.assertEquals('MIT License',
find_license_from_classifier(message))

def test_not_fond_license_from_classifier(self):
metadata_as_no_license = ('Metadata-Version: 2.0\r\n'
'Name: pip-licenses\r\n'
'Version: 1.0.0\r\n')
message = message_from_string(metadata_as_no_license)
self.assertEquals(LICENSE_UNKNOWN,
find_license_from_classifier(message))

def tearDown(self):
pass

0 comments on commit 08bc90f

Please sign in to comment.