mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
Merge pull request #53 from anchore/add-python-wheel-egg-analyzers
Add python wheel and egg analyzer support
This commit is contained in:
commit
64a9125895
18 changed files with 622 additions and 15 deletions
|
@ -13,7 +13,7 @@ type Analyzer struct {
|
|||
|
||||
func NewAnalyzer() *Analyzer {
|
||||
globParserDispatch := map[string]common.ParserFn{
|
||||
"*/Gemfile.lock": ParseGemfileLockEntries,
|
||||
"*/Gemfile.lock": parseGemfileLockEntries,
|
||||
}
|
||||
|
||||
return &Analyzer{
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
var sectionsOfInterest = internal.NewStringSetFromSlice([]string{"GEM"})
|
||||
|
||||
func ParseGemfileLockEntries(reader io.Reader) ([]pkg.Package, error) {
|
||||
func parseGemfileLockEntries(reader io.Reader) ([]pkg.Package, error) {
|
||||
pkgs := make([]pkg.Package, 0)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ func TestParseGemfileLockEntries(t *testing.T) {
|
|||
t.Fatalf("failed to open fixture: %+v", err)
|
||||
}
|
||||
|
||||
actual, err := ParseGemfileLockEntries(fixture)
|
||||
actual, err := parseGemfileLockEntries(fixture)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse gemfile lock: %+v", err)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/tree"
|
||||
)
|
||||
|
||||
// TODO: put under test...
|
||||
|
||||
type GenericAnalyzer struct {
|
||||
globParserDispatch map[string]ParserFn
|
||||
pathParserDispatch map[string]ParserFn
|
||||
|
|
|
@ -3,6 +3,7 @@ package analyzer
|
|||
import (
|
||||
"github.com/anchore/imgbom/imgbom/analyzer/bundler"
|
||||
"github.com/anchore/imgbom/imgbom/analyzer/dpkg"
|
||||
"github.com/anchore/imgbom/imgbom/analyzer/python"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/imgbom/imgbom/scope"
|
||||
"github.com/anchore/imgbom/internal/log"
|
||||
|
@ -30,6 +31,7 @@ func newController() controller {
|
|||
}
|
||||
ctrlr.add(dpkg.NewAnalyzer())
|
||||
ctrlr.add(bundler.NewAnalyzer())
|
||||
ctrlr.add(python.NewAnalyzer())
|
||||
return ctrlr
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ type Analyzer struct {
|
|||
|
||||
func NewAnalyzer() *Analyzer {
|
||||
pathParserDispatch := map[string]common.ParserFn{
|
||||
"/var/lib/dpkg/status": ParseDpkgStatus,
|
||||
"/var/lib/dpkg/status": parseDpkgStatus,
|
||||
}
|
||||
|
||||
return &Analyzer{
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
var errEndOfPackages = fmt.Errorf("no more packages to read")
|
||||
|
||||
func ParseDpkgStatus(reader io.Reader) ([]pkg.Package, error) {
|
||||
func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) {
|
||||
buffedReader := bufio.NewReader(reader)
|
||||
var packages = make([]pkg.Package, 0)
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ func TestMultiplePackages(t *testing.T) {
|
|||
}
|
||||
}()
|
||||
|
||||
pkgs, err := ParseDpkgStatus(file)
|
||||
pkgs, err := parseDpkgStatus(file)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to read file contents: ", err)
|
||||
}
|
||||
|
|
35
imgbom/analyzer/python/analyzer.go
Normal file
35
imgbom/analyzer/python/analyzer.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package python
|
||||
|
||||
import (
|
||||
"github.com/anchore/imgbom/imgbom/analyzer/common"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/tree"
|
||||
)
|
||||
|
||||
type Analyzer struct {
|
||||
analyzer common.GenericAnalyzer
|
||||
}
|
||||
|
||||
func NewAnalyzer() *Analyzer {
|
||||
globParserDispatch := map[string]common.ParserFn{
|
||||
"*egg-info/PKG-INFO": parseEggMetadata,
|
||||
"*dist-info/METADATA": parseWheelMetadata,
|
||||
}
|
||||
|
||||
return &Analyzer{
|
||||
analyzer: common.NewGenericAnalyzer(nil, globParserDispatch),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Analyzer) Name() string {
|
||||
return "python-analyzer"
|
||||
}
|
||||
|
||||
func (a *Analyzer) SelectFiles(trees []tree.FileTreeReader) []file.Reference {
|
||||
return a.analyzer.SelectFiles(trees)
|
||||
}
|
||||
|
||||
func (a *Analyzer) Analyze(contents map[file.Reference]string) ([]pkg.Package, error) {
|
||||
return a.analyzer.Analyze(contents, a.Name())
|
||||
}
|
90
imgbom/analyzer/python/parse_wheel_egg.go
Normal file
90
imgbom/analyzer/python/parse_wheel_egg.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package python
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
)
|
||||
|
||||
func parseWheelMetadata(reader io.Reader) ([]pkg.Package, error) {
|
||||
packages, err := parseWheelOrEggMetadata(reader)
|
||||
for idx := range packages {
|
||||
packages[idx].Type = pkg.WheelPkg
|
||||
}
|
||||
return packages, err
|
||||
}
|
||||
|
||||
func parseEggMetadata(reader io.Reader) ([]pkg.Package, error) {
|
||||
packages, err := parseWheelOrEggMetadata(reader)
|
||||
for idx := range packages {
|
||||
packages[idx].Type = pkg.EggPkg
|
||||
}
|
||||
return packages, err
|
||||
}
|
||||
|
||||
func parseWheelOrEggMetadata(reader io.Reader) ([]pkg.Package, error) {
|
||||
fields := make(map[string]string)
|
||||
var key string
|
||||
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
line = strings.TrimRight(line, "\n")
|
||||
|
||||
// empty line indicates end of entry
|
||||
if len(line) == 0 {
|
||||
// if the entry has not started, keep parsing lines
|
||||
if len(fields) == 0 {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(line, " "):
|
||||
// a field-body continuation
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("no match for continuation: line: '%s'", line)
|
||||
}
|
||||
|
||||
val, ok := fields[key]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no previous key exists, expecting: %s", key)
|
||||
}
|
||||
// concatenate onto previous value
|
||||
val = fmt.Sprintf("%s\n %s", val, strings.TrimSpace(line))
|
||||
fields[key] = val
|
||||
default:
|
||||
// parse a new key (note, duplicate keys are overridden)
|
||||
if i := strings.Index(line, ":"); i > 0 {
|
||||
key = strings.TrimSpace(line[0:i])
|
||||
val := strings.TrimSpace(line[i+1:])
|
||||
|
||||
fields[key] = val
|
||||
} else {
|
||||
return nil, fmt.Errorf("cannot parse field from line: '%s'", line)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse python wheel/egg: %w", err)
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: fields["Name"],
|
||||
Version: fields["Version"],
|
||||
Language: pkg.Python,
|
||||
}
|
||||
|
||||
if license, ok := fields["License"]; ok && license != "" {
|
||||
p.Licenses = []string{license}
|
||||
}
|
||||
|
||||
return []pkg.Package{p}, nil
|
||||
}
|
91
imgbom/analyzer/python/parse_wheel_egg_test.go
Normal file
91
imgbom/analyzer/python/parse_wheel_egg_test.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package python
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
)
|
||||
|
||||
func assertPkgsEqual(t *testing.T, actual []pkg.Package, expected map[string]pkg.Package) {
|
||||
t.Helper()
|
||||
if len(actual) != 1 {
|
||||
for _, a := range actual {
|
||||
t.Log(" ", a)
|
||||
}
|
||||
t.Fatalf("unexpected package count: %d!=%d", len(actual), 1)
|
||||
}
|
||||
|
||||
for _, a := range actual {
|
||||
expectedPkg, ok := expected[a.Name]
|
||||
if !ok {
|
||||
t.Errorf("unexpected package found: '%s'", a.Name)
|
||||
}
|
||||
|
||||
if expectedPkg.Version != a.Version {
|
||||
t.Errorf("unexpected package version: '%s'", a.Version)
|
||||
}
|
||||
|
||||
if a.Language != expectedPkg.Language {
|
||||
t.Errorf("bad language: '%+v'", a.Language)
|
||||
}
|
||||
|
||||
if a.Type != expectedPkg.Type {
|
||||
t.Errorf("bad package type: %+v", a.Type)
|
||||
}
|
||||
|
||||
if len(a.Licenses) < 1 {
|
||||
t.Errorf("bad package licenses count: '%+v'", a.Licenses)
|
||||
} else if a.Licenses[0] != expectedPkg.Licenses[0] {
|
||||
t.Errorf("bad package licenses: '%+v'", a.Licenses)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
func TestParseEggMetadata(t *testing.T) {
|
||||
expected := map[string]pkg.Package{
|
||||
"requests": {
|
||||
Name: "requests",
|
||||
Version: "2.22.0",
|
||||
Language: pkg.Python,
|
||||
Type: pkg.EggPkg,
|
||||
Licenses: []string{"Apache 2.0"},
|
||||
},
|
||||
}
|
||||
fixture, err := os.Open("test-fixtures/egg-info/PKG-INFO")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open fixture: %+v", err)
|
||||
}
|
||||
|
||||
actual, err := parseEggMetadata(fixture)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse egg-info: %+v", err)
|
||||
}
|
||||
|
||||
assertPkgsEqual(t, actual, expected)
|
||||
|
||||
}
|
||||
|
||||
func TestParseWheelMetadata(t *testing.T) {
|
||||
expected := map[string]pkg.Package{
|
||||
"Pygments": {
|
||||
Name: "Pygments",
|
||||
Version: "2.6.1",
|
||||
Language: pkg.Python,
|
||||
Type: pkg.WheelPkg,
|
||||
Licenses: []string{"BSD License"},
|
||||
},
|
||||
}
|
||||
fixture, err := os.Open("test-fixtures/dist-info/METADATA")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open fixture: %+v", err)
|
||||
}
|
||||
|
||||
actual, err := parseWheelMetadata(fixture)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse dist-info: %+v", err)
|
||||
}
|
||||
|
||||
assertPkgsEqual(t, actual, expected)
|
||||
|
||||
}
|
47
imgbom/analyzer/python/test-fixtures/dist-info/METADATA
Normal file
47
imgbom/analyzer/python/test-fixtures/dist-info/METADATA
Normal file
|
@ -0,0 +1,47 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: Pygments
|
||||
Version: 2.6.1
|
||||
Summary: Pygments is a syntax highlighting package written in Python.
|
||||
Home-page: https://pygments.org/
|
||||
Author: Georg Brandl
|
||||
Author-email: georg@python.org
|
||||
License: BSD License
|
||||
Keywords: syntax highlighting
|
||||
Platform: any
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Intended Audience :: End Users/Desktop
|
||||
Classifier: Intended Audience :: System Administrators
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Text Processing :: Filters
|
||||
Classifier: Topic :: Utilities
|
||||
Requires-Python: >=3.5
|
||||
|
||||
|
||||
Pygments
|
||||
~~~~~~~~
|
||||
|
||||
Pygments is a syntax highlighting package written in Python.
|
||||
|
||||
It is a generic syntax highlighter suitable for use in code hosting, forums,
|
||||
wikis or other applications that need to prettify source code. Highlights
|
||||
are:
|
||||
|
||||
* a wide range of over 500 languages and other text formats is supported
|
||||
* special attention is paid to details, increasing quality by a fair amount
|
||||
* support for new languages and formats are added easily
|
||||
* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences
|
||||
* it is usable as a command-line tool and as a library
|
||||
|
||||
:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
|
134
imgbom/analyzer/python/test-fixtures/egg-info/PKG-INFO
Normal file
134
imgbom/analyzer/python/test-fixtures/egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,134 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: requests
|
||||
Version: 2.22.0
|
||||
Summary: Python HTTP for Humans.
|
||||
Home-page: http://python-requests.org
|
||||
Author: Kenneth Reitz
|
||||
Author-email: me@kennethreitz.org
|
||||
License: Apache 2.0
|
||||
Description: Requests: HTTP for Humans™
|
||||
==========================
|
||||
|
||||
[![image](https://img.shields.io/pypi/v/requests.svg)](https://pypi.org/project/requests/)
|
||||
[![image](https://img.shields.io/pypi/l/requests.svg)](https://pypi.org/project/requests/)
|
||||
[![image](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests/)
|
||||
[![codecov.io](https://codecov.io/github/requests/requests/coverage.svg?branch=master)](https://codecov.io/github/requests/requests)
|
||||
[![image](https://img.shields.io/github/contributors/requests/requests.svg)](https://github.com/requests/requests/graphs/contributors)
|
||||
[![image](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/kennethreitz)
|
||||
|
||||
Requests is the only *Non-GMO* HTTP library for Python, safe for human
|
||||
consumption.
|
||||
|
||||
![image](https://farm5.staticflickr.com/4317/35198386374_1939af3de6_k_d.jpg)
|
||||
|
||||
Behold, the power of Requests:
|
||||
|
||||
``` {.sourceCode .python}
|
||||
>>> import requests
|
||||
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
|
||||
>>> r.status_code
|
||||
200
|
||||
>>> r.headers['content-type']
|
||||
'application/json; charset=utf8'
|
||||
>>> r.encoding
|
||||
'utf-8'
|
||||
>>> r.text
|
||||
u'{"type":"User"...'
|
||||
>>> r.json()
|
||||
{u'disk_usage': 368627, u'private_gists': 484, ...}
|
||||
```
|
||||
|
||||
See [the similar code, sans Requests](https://gist.github.com/973705).
|
||||
|
||||
[![image](https://raw.githubusercontent.com/requests/requests/master/docs/_static/requests-logo-small.png)](http://docs.python-requests.org/)
|
||||
|
||||
Requests allows you to send *organic, grass-fed* HTTP/1.1 requests,
|
||||
without the need for manual labor. There's no need to manually add query
|
||||
strings to your URLs, or to form-encode your POST data. Keep-alive and
|
||||
HTTP connection pooling are 100% automatic, thanks to
|
||||
[urllib3](https://github.com/shazow/urllib3).
|
||||
|
||||
Besides, all the cool kids are doing it. Requests is one of the most
|
||||
downloaded Python packages of all time, pulling in over 11,000,000
|
||||
downloads every month. You don't want to be left out!
|
||||
|
||||
Feature Support
|
||||
---------------
|
||||
|
||||
Requests is ready for today's web.
|
||||
|
||||
- International Domains and URLs
|
||||
- Keep-Alive & Connection Pooling
|
||||
- Sessions with Cookie Persistence
|
||||
- Browser-style SSL Verification
|
||||
- Basic/Digest Authentication
|
||||
- Elegant Key/Value Cookies
|
||||
- Automatic Decompression
|
||||
- Automatic Content Decoding
|
||||
- Unicode Response Bodies
|
||||
- Multipart File Uploads
|
||||
- HTTP(S) Proxy Support
|
||||
- Connection Timeouts
|
||||
- Streaming Downloads
|
||||
- `.netrc` Support
|
||||
- Chunked Requests
|
||||
|
||||
Requests officially supports Python 2.7 & 3.4–3.7, and runs great on
|
||||
PyPy.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install Requests, simply use [pipenv](http://pipenv.org/) (or pip, of
|
||||
course):
|
||||
|
||||
``` {.sourceCode .bash}
|
||||
$ pipenv install requests
|
||||
✨🍰✨
|
||||
```
|
||||
|
||||
Satisfaction guaranteed.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Fantastic documentation is available at
|
||||
<http://docs.python-requests.org/>, for a limited time only.
|
||||
|
||||
How to Contribute
|
||||
-----------------
|
||||
|
||||
1. Become more familiar with the project by reading our [Contributor's Guide](http://docs.python-requests.org/en/latest/dev/contributing/) and our [development philosophy](http://docs.python-requests.org/en/latest/dev/philosophy/).
|
||||
2. Check for open issues or open a fresh issue to start a discussion
|
||||
around a feature idea or a bug. There is a [Contributor
|
||||
Friendly](https://github.com/requests/requests/issues?direction=desc&labels=Contributor+Friendly&page=1&sort=updated&state=open)
|
||||
tag for issues that should be ideal for people who are not very
|
||||
familiar with the codebase yet.
|
||||
3. Fork [the repository](https://github.com/requests/requests) on
|
||||
GitHub to start making your changes to the **master** branch (or
|
||||
branch off of it).
|
||||
4. Write a test which shows that the bug was fixed or that the feature
|
||||
works as expected.
|
||||
5. Send a pull request and bug the maintainer until it gets merged and
|
||||
published. :) Make sure to add yourself to
|
||||
[AUTHORS](https://github.com/requests/requests/blob/master/AUTHORS.rst).
|
||||
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: License :: OSI Approved :: Apache Software License
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
|
||||
Description-Content-Type: text/markdown
|
||||
Provides-Extra: security
|
||||
Provides-Extra: socks
|
|
@ -5,8 +5,10 @@ const (
|
|||
ApkPkg
|
||||
BundlerPkg
|
||||
DebPkg
|
||||
EggPkg
|
||||
PacmanPkg
|
||||
RpmPkg
|
||||
WheelPkg
|
||||
)
|
||||
|
||||
type Type uint
|
||||
|
@ -14,18 +16,22 @@ type Type uint
|
|||
var typeStr = []string{
|
||||
"UnknownPackage",
|
||||
"apk",
|
||||
"bundler",
|
||||
"bundle",
|
||||
"deb",
|
||||
"egg",
|
||||
"pacman",
|
||||
"rpm",
|
||||
"wheel",
|
||||
}
|
||||
|
||||
var AllPkgs = []Type{
|
||||
ApkPkg,
|
||||
BundlerPkg,
|
||||
DebPkg,
|
||||
EggPkg,
|
||||
PacmanPkg,
|
||||
RpmPkg,
|
||||
WheelPkg,
|
||||
}
|
||||
|
||||
func (t Type) String() string {
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/anchore/imgbom/imgbom/scope"
|
||||
)
|
||||
|
||||
func TestBundlerImage(t *testing.T) {
|
||||
func TestLanguageImage(t *testing.T) {
|
||||
img, cleanup := testutils.GetFixtureImage(t, "docker-archive", "image-language-pkgs")
|
||||
defer cleanup()
|
||||
|
||||
|
@ -26,6 +26,22 @@ func TestBundlerImage(t *testing.T) {
|
|||
pkgLanguage pkg.Language
|
||||
pkgInfo map[string]string
|
||||
}{
|
||||
{
|
||||
name: "find python wheel packages",
|
||||
pkgType: pkg.WheelPkg,
|
||||
pkgLanguage: pkg.Python,
|
||||
pkgInfo: map[string]string{
|
||||
"Pygments": "2.6.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find python egg packages",
|
||||
pkgType: pkg.EggPkg,
|
||||
pkgLanguage: pkg.Python,
|
||||
pkgInfo: map[string]string{
|
||||
"requests": "2.22.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find bundler packages",
|
||||
pkgType: pkg.BundlerPkg,
|
||||
|
@ -88,12 +104,7 @@ func TestBundlerImage(t *testing.T) {
|
|||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
if catalog.PackageCount() != len(c.pkgInfo) {
|
||||
for a := range catalog.Enumerate(c.pkgType) {
|
||||
t.Log(" ", a)
|
||||
}
|
||||
t.Fatalf("unexpected package count: %d!=%d", catalog.PackageCount(), len(c.pkgInfo))
|
||||
}
|
||||
pkgCount := 0
|
||||
|
||||
for a := range catalog.Enumerate(c.pkgType) {
|
||||
|
||||
|
@ -113,6 +124,14 @@ func TestBundlerImage(t *testing.T) {
|
|||
if a.Type != c.pkgType {
|
||||
t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
|
||||
}
|
||||
pkgCount++
|
||||
}
|
||||
|
||||
if pkgCount != len(c.pkgInfo) {
|
||||
for a := range catalog.Enumerate(c.pkgType) {
|
||||
t.Log(" ", a)
|
||||
}
|
||||
t.Fatalf("unexpected package count: %d!=%d", pkgCount, len(c.pkgInfo))
|
||||
}
|
||||
|
||||
})
|
|
@ -1,2 +1,2 @@
|
|||
FROM scratch
|
||||
COPY Gemfile.lock .
|
||||
COPY . .
|
|
@ -0,0 +1,47 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: Pygments
|
||||
Version: 2.6.1
|
||||
Summary: Pygments is a syntax highlighting package written in Python.
|
||||
Home-page: https://pygments.org/
|
||||
Author: Georg Brandl
|
||||
Author-email: georg@python.org
|
||||
License: BSD License
|
||||
Keywords: syntax highlighting
|
||||
Platform: any
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Intended Audience :: End Users/Desktop
|
||||
Classifier: Intended Audience :: System Administrators
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Text Processing :: Filters
|
||||
Classifier: Topic :: Utilities
|
||||
Requires-Python: >=3.5
|
||||
|
||||
|
||||
Pygments
|
||||
~~~~~~~~
|
||||
|
||||
Pygments is a syntax highlighting package written in Python.
|
||||
|
||||
It is a generic syntax highlighter suitable for use in code hosting, forums,
|
||||
wikis or other applications that need to prettify source code. Highlights
|
||||
are:
|
||||
|
||||
* a wide range of over 500 languages and other text formats is supported
|
||||
* special attention is paid to details, increasing quality by a fair amount
|
||||
* support for new languages and formats are added easily
|
||||
* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences
|
||||
* it is usable as a command-line tool and as a library
|
||||
|
||||
:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
|
134
integration/test-fixtures/image-language-pkgs/egg-info/PKG-INFO
Normal file
134
integration/test-fixtures/image-language-pkgs/egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,134 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: requests
|
||||
Version: 2.22.0
|
||||
Summary: Python HTTP for Humans.
|
||||
Home-page: http://python-requests.org
|
||||
Author: Kenneth Reitz
|
||||
Author-email: me@kennethreitz.org
|
||||
License: Apache 2.0
|
||||
Description: Requests: HTTP for Humans™
|
||||
==========================
|
||||
|
||||
[![image](https://img.shields.io/pypi/v/requests.svg)](https://pypi.org/project/requests/)
|
||||
[![image](https://img.shields.io/pypi/l/requests.svg)](https://pypi.org/project/requests/)
|
||||
[![image](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests/)
|
||||
[![codecov.io](https://codecov.io/github/requests/requests/coverage.svg?branch=master)](https://codecov.io/github/requests/requests)
|
||||
[![image](https://img.shields.io/github/contributors/requests/requests.svg)](https://github.com/requests/requests/graphs/contributors)
|
||||
[![image](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/kennethreitz)
|
||||
|
||||
Requests is the only *Non-GMO* HTTP library for Python, safe for human
|
||||
consumption.
|
||||
|
||||
![image](https://farm5.staticflickr.com/4317/35198386374_1939af3de6_k_d.jpg)
|
||||
|
||||
Behold, the power of Requests:
|
||||
|
||||
``` {.sourceCode .python}
|
||||
>>> import requests
|
||||
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
|
||||
>>> r.status_code
|
||||
200
|
||||
>>> r.headers['content-type']
|
||||
'application/json; charset=utf8'
|
||||
>>> r.encoding
|
||||
'utf-8'
|
||||
>>> r.text
|
||||
u'{"type":"User"...'
|
||||
>>> r.json()
|
||||
{u'disk_usage': 368627, u'private_gists': 484, ...}
|
||||
```
|
||||
|
||||
See [the similar code, sans Requests](https://gist.github.com/973705).
|
||||
|
||||
[![image](https://raw.githubusercontent.com/requests/requests/master/docs/_static/requests-logo-small.png)](http://docs.python-requests.org/)
|
||||
|
||||
Requests allows you to send *organic, grass-fed* HTTP/1.1 requests,
|
||||
without the need for manual labor. There's no need to manually add query
|
||||
strings to your URLs, or to form-encode your POST data. Keep-alive and
|
||||
HTTP connection pooling are 100% automatic, thanks to
|
||||
[urllib3](https://github.com/shazow/urllib3).
|
||||
|
||||
Besides, all the cool kids are doing it. Requests is one of the most
|
||||
downloaded Python packages of all time, pulling in over 11,000,000
|
||||
downloads every month. You don't want to be left out!
|
||||
|
||||
Feature Support
|
||||
---------------
|
||||
|
||||
Requests is ready for today's web.
|
||||
|
||||
- International Domains and URLs
|
||||
- Keep-Alive & Connection Pooling
|
||||
- Sessions with Cookie Persistence
|
||||
- Browser-style SSL Verification
|
||||
- Basic/Digest Authentication
|
||||
- Elegant Key/Value Cookies
|
||||
- Automatic Decompression
|
||||
- Automatic Content Decoding
|
||||
- Unicode Response Bodies
|
||||
- Multipart File Uploads
|
||||
- HTTP(S) Proxy Support
|
||||
- Connection Timeouts
|
||||
- Streaming Downloads
|
||||
- `.netrc` Support
|
||||
- Chunked Requests
|
||||
|
||||
Requests officially supports Python 2.7 & 3.4–3.7, and runs great on
|
||||
PyPy.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install Requests, simply use [pipenv](http://pipenv.org/) (or pip, of
|
||||
course):
|
||||
|
||||
``` {.sourceCode .bash}
|
||||
$ pipenv install requests
|
||||
✨🍰✨
|
||||
```
|
||||
|
||||
Satisfaction guaranteed.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Fantastic documentation is available at
|
||||
<http://docs.python-requests.org/>, for a limited time only.
|
||||
|
||||
How to Contribute
|
||||
-----------------
|
||||
|
||||
1. Become more familiar with the project by reading our [Contributor's Guide](http://docs.python-requests.org/en/latest/dev/contributing/) and our [development philosophy](http://docs.python-requests.org/en/latest/dev/philosophy/).
|
||||
2. Check for open issues or open a fresh issue to start a discussion
|
||||
around a feature idea or a bug. There is a [Contributor
|
||||
Friendly](https://github.com/requests/requests/issues?direction=desc&labels=Contributor+Friendly&page=1&sort=updated&state=open)
|
||||
tag for issues that should be ideal for people who are not very
|
||||
familiar with the codebase yet.
|
||||
3. Fork [the repository](https://github.com/requests/requests) on
|
||||
GitHub to start making your changes to the **master** branch (or
|
||||
branch off of it).
|
||||
4. Write a test which shows that the bug was fixed or that the feature
|
||||
works as expected.
|
||||
5. Send a pull request and bug the maintainer until it gets merged and
|
||||
published. :) Make sure to add yourself to
|
||||
[AUTHORS](https://github.com/requests/requests/blob/master/AUTHORS.rst).
|
||||
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: License :: OSI Approved :: Apache Software License
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
|
||||
Description-Content-Type: text/markdown
|
||||
Provides-Extra: security
|
||||
Provides-Extra: socks
|
Loading…
Reference in a new issue