community.docker/plugins/module_utils/_api/utils/fnmatch.py
Felix Fontein 4d508b4c37
Vendor API connection code from Docker SDK for Python (#398)
* Vendor parts of the Docker SDK for Python

This is a combination of the latest git version
(a48a5a9647)
and the version before Python 2.7 support was removed
(650aad3a5f),
including some modifications to work with Ansible module_utils's
system (i.e. third-party imports are guarded, and errors are
reported during runtime through a new exception
MissingRequirementException).

* Create module_utils and plugin_utils for working with the vendored code.

The delete call cannot be called delete() since that method already exists from requests.

* Vendor more code from Docker SDK for Python.

* Adjust code from common module_utils.

* Add unit tests from Docker SDK for Python.

* Make test compile with Python 2.6, but skip them on Python 2.6.

* Skip test that requires a network server.

* Add changelog.

* Update changelogs/fragments/398-docker-api.yml

Co-authored-by: Brian Scholer <1260690+briantist@users.noreply.github.com>

* Minimum API version is 1.25.

Co-authored-by: Brian Scholer <1260690+briantist@users.noreply.github.com>
2022-07-02 16:40:44 +02:00

127 lines
3.7 KiB
Python

# -*- coding: utf-8 -*-
# This code is part of the Ansible collection community.docker, but is an independent component.
# This particular file, and this file only, is based on the Docker SDK for Python (https://github.com/docker/docker-py/)
#
# Copyright (c) 2016-2022 Docker, Inc.
#
# It is licensed under the Apache 2.0 license (see Apache-2.0.txt in this collection)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
"""Filename matching with shell patterns.
fnmatch(FILENAME, PATTERN) matches according to the local convention.
fnmatchcase(FILENAME, PATTERN) always takes case in account.
The functions operate by translating the pattern into a regular
expression. They cache the compiled regular expressions for speed.
The function translate(PATTERN) returns a regular expression
corresponding to PATTERN. (It does not compile it.)
"""
import re
__all__ = ["fnmatch", "fnmatchcase", "translate"]
_cache = {}
_MAXCACHE = 100
def _purge():
"""Clear the pattern cache"""
_cache.clear()
def fnmatch(name, pat):
"""Test whether FILENAME matches PATTERN.
Patterns are Unix shell style:
* matches everything
? matches any single character
[seq] matches any character in seq
[!seq] matches any char not in seq
An initial period in FILENAME is not special.
Both FILENAME and PATTERN are first case-normalized
if the operating system requires it.
If you don't want this, use fnmatchcase(FILENAME, PATTERN).
"""
name = name.lower()
pat = pat.lower()
return fnmatchcase(name, pat)
def fnmatchcase(name, pat):
"""Test whether FILENAME matches PATTERN, including case.
This is a version of fnmatch() which doesn't case-normalize
its arguments.
"""
try:
re_pat = _cache[pat]
except KeyError:
res = translate(pat)
if len(_cache) >= _MAXCACHE:
_cache.clear()
_cache[pat] = re_pat = re.compile(res)
return re_pat.match(name) is not None
def translate(pat):
"""Translate a shell PATTERN to a regular expression.
There is no way to quote meta-characters.
"""
i, n = 0, len(pat)
res = '^'
while i < n:
c = pat[i]
i = i + 1
if c == '*':
if i < n and pat[i] == '*':
# is some flavor of "**"
i = i + 1
# Treat **/ as ** so eat the "/"
if i < n and pat[i] == '/':
i = i + 1
if i >= n:
# is "**EOF" - to align with .gitignore just accept all
res = res + '.*'
else:
# is "**"
# Note that this allows for any # of /'s (even 0) because
# the .* will eat everything, even /'s
res = res + '(.*/)?'
else:
# is "*" so map it to anything but "/"
res = res + '[^/]*'
elif c == '?':
# "?" is any char except "/"
res = res + '[^/]'
elif c == '[':
j = i
if j < n and pat[j] == '!':
j = j + 1
if j < n and pat[j] == ']':
j = j + 1
while j < n and pat[j] != ']':
j = j + 1
if j >= n:
res = res + '\\['
else:
stuff = pat[i:j].replace('\\', '\\\\')
i = j + 1
if stuff[0] == '!':
stuff = '^' + stuff[1:]
elif stuff[0] == '^':
stuff = '\\' + stuff
res = '%s[%s]' % (res, stuff)
else:
res = res + re.escape(c)
return res + '$'