From 5f81bf12d81c04942d825e3987949c56c44ed2fb Mon Sep 17 00:00:00 2001 From: Kevin Steves Date: Wed, 20 Apr 2022 13:23:27 -0700 Subject: [PATCH] Remove support for Python 2. Switch to pyproject.toml based build system. --- MANIFEST.in | 2 +- README.rst | 1 - bin/panafapi.py | 3 +- bin/panconf.py | 3 +- bin/panlicapi.py | 3 +- bin/panwfapi.py | 53 ++++++++++++------------------ bin/panxapi.py | 58 +++++++++++---------------------- doc/pan.wfapi.rst | 3 -- doc/pan.xapi.rst | 7 +--- doc/panwfapi.rst | 3 -- doc/panxapi.rst | 6 ++-- lib/pan/commit.py | 1 - lib/pan/http.py | 43 +++++-------------------- lib/pan/licapi/v1.py | 2 +- lib/pan/rc.py | 1 - lib/pan/wfapi.py | 71 +++++++--------------------------------- lib/pan/xapi.py | 77 ++++++++------------------------------------ pyproject.toml | 3 ++ setup.cfg | 31 ++++++++++++++++-- setup.py | 52 ------------------------------ 20 files changed, 113 insertions(+), 310 deletions(-) create mode 100644 pyproject.toml delete mode 100755 setup.py diff --git a/MANIFEST.in b/MANIFEST.in index 0e0ba88..e805d39 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -include MANIFEST.in HISTORY.rst LICENSE.txt AUTHORS.rst +include HISTORY.rst include doc/Makefile doc/*.rst doc/*.html diff --git a/README.rst b/README.rst index 9a4dbd8..50bbda9 100644 --- a/README.rst +++ b/README.rst @@ -31,5 +31,4 @@ on GitHub and as a `package `_ on PyPi. -Python versions 2.7 and 3.x are supported with a single code base. There are no external modules required to use ``pan-python``. diff --git a/bin/panafapi.py b/bin/panafapi.py index 6b805ca..0bc92f1 100755 --- a/bin/panafapi.py +++ b/bin/panafapi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2015 Palo Alto Networks, Inc. @@ -16,7 +16,6 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -from __future__ import print_function import datetime import getopt import json diff --git a/bin/panconf.py b/bin/panconf.py index c6e6dc7..bd794ee 100755 --- a/bin/panconf.py +++ b/bin/panconf.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2012-2014 Kevin Steves @@ -16,7 +16,6 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -from __future__ import print_function import sys import os import signal diff --git a/bin/panlicapi.py b/bin/panlicapi.py index 270e1db..d3530d7 100755 --- a/bin/panlicapi.py +++ b/bin/panlicapi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2017 Palo Alto Networks, Inc. @@ -16,7 +16,6 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -from __future__ import print_function import getopt import json import logging diff --git a/bin/panwfapi.py b/bin/panwfapi.py index aa23573..505727d 100755 --- a/bin/panwfapi.py +++ b/bin/panwfapi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2013-2017 Kevin Steves @@ -16,7 +16,6 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -from __future__ import print_function from datetime import date, timedelta import sys import os @@ -26,10 +25,7 @@ import pprint import logging import ssl -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse +from urllib.parse import urlparse libpath = os.path.dirname(os.path.abspath(__file__)) sys.path[:0] = [os.path.join(libpath, os.pardir, 'lib')] @@ -474,33 +470,26 @@ def parse_opts(): def create_ssl_context(cafile, capath, ssl_option): - # PEP 0476 - if (sys.version_info.major == 2 and sys.hexversion >= 0x02070900 or - sys.version_info.major == 3 and sys.hexversion >= 0x03040300): - if cafile or capath: - try: - ssl_context = ssl.create_default_context( - purpose=ssl.Purpose.SERVER_AUTH, - cafile=cafile, - capath=capath) - except Exception as e: - print('cafile or capath invalid: %s' % e, file=sys.stderr) - sys.exit(1) - elif ssl_option: - if ssl_option == 'cacloud': - ssl_context = pan.wfapi.cloud_ssl_context() - elif ssl_option == 'noverify': - ssl_context = ssl._create_unverified_context() - elif ssl_option == 'default': - ssl_context = None - - return ssl_context - - print('Warning: Python %d.%d.%d: cafile, capath and ssl ignored' % - (sys.version_info.major, sys.version_info.minor, - sys.version_info.micro), file=sys.stderr) + if cafile or capath: + try: + ssl_context = ssl.create_default_context( + purpose=ssl.Purpose.SERVER_AUTH, + cafile=cafile, + capath=capath) + except Exception as e: + print('cafile or capath invalid: %s' % e, file=sys.stderr) + sys.exit(1) + elif ssl_option: + if ssl_option == 'cacloud': + ssl_context = pan.wfapi.cloud_ssl_context() + elif ssl_option == 'noverify': + ssl_context = ssl._create_unverified_context() + elif ssl_option == 'default': + ssl_context = None + else: + assert False, 'cafile or capath or ssl_option' - return None + return ssl_context def print_status(wfapi, action, exception_msg=None): diff --git a/bin/panxapi.py b/bin/panxapi.py index e1c22cb..7587e59 100755 --- a/bin/panxapi.py +++ b/bin/panxapi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (c) 2013-2015 Kevin Steves @@ -16,7 +16,6 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -from __future__ import print_function from datetime import datetime import sys import os @@ -610,28 +609,18 @@ def parse_opts(): def create_ssl_context(cafile, capath): - if (sys.version_info.major == 2 and sys.hexversion >= 0x02070900 or - sys.version_info.major == 3 and sys.hexversion >= 0x03020000): - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - context.options |= ssl.OP_NO_SSLv3 - context.verify_mode = ssl.CERT_REQUIRED - # added 3.4 - if hasattr(context, 'check_hostname'): - context.check_hostname = True - try: - context.load_verify_locations(cafile=cafile, capath=capath) - except Exception as e: - print('cafile or capath invalid: %s' % e, file=sys.stderr) - sys.exit(1) - - return context - - print('Warning: Python %d.%d: cafile and capath ignored' % - (sys.version_info.major, sys.version_info.minor), - file=sys.stderr) + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.options |= ssl.OP_NO_SSLv2 + context.options |= ssl.OP_NO_SSLv3 + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True + try: + context.load_verify_locations(cafile=cafile, capath=capath) + except Exception as e: + print('cafile or capath invalid: %s' % e, file=sys.stderr) + sys.exit(1) - return None + return context def get_vsys(s): @@ -828,23 +817,14 @@ def set_encoding(): # encoding = 'utf-8' - if hasattr(sys.stdin, 'detach'): - # >= 3.1 - import io - - for s in ('stdin', 'stdout', 'stderr'): - line_buffering = getattr(sys, s).line_buffering -# print(s, line_buffering, file=sys.stderr) - setattr(sys, s, io.TextIOWrapper(getattr(sys, s).detach(), - encoding=encoding, - line_buffering=line_buffering)) - - else: - import codecs + import io - sys.stdin = codecs.getreader(encoding)(sys.stdin) - sys.stdout = codecs.getwriter(encoding)(sys.stdout) - sys.stderr = codecs.getwriter(encoding)(sys.stderr) + for s in ('stdin', 'stdout', 'stderr'): + line_buffering = getattr(sys, s).line_buffering + # print(s, line_buffering, file=sys.stderr) + setattr(sys, s, io.TextIOWrapper(getattr(sys, s).detach(), + encoding=encoding, + line_buffering=line_buffering)) def usage(): diff --git a/doc/pan.wfapi.rst b/doc/pan.wfapi.rst index 72849f9..fbce36f 100644 --- a/doc/pan.wfapi.rst +++ b/doc/pan.wfapi.rst @@ -145,9 +145,6 @@ class pan.wfapi.PanWFapi() The default is *None*. - SSL contexts are supported starting in Python versions 2.7.9 - and 3.2. - exception pan.wfapi.PanWFapiError ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/pan.xapi.rst b/doc/pan.xapi.rst index 91de3ac..8764ba6 100644 --- a/doc/pan.xapi.rst +++ b/doc/pan.xapi.rst @@ -156,13 +156,8 @@ class pan.xapi.PanXapi() This can be used to specify the ``cafile``, ``capath`` and other SSL configuration options. - SSL contexts are supported starting in Python versions 2.7.9 - and 3.2. - - Starting with Python versions 2.7.9 and 3.4.3 SSL server certificate - verification is performed by default as described in PEP 476. Because many PAN-OS systems use a self-signed certificate, pan.xapi - will disable the default starting with these versions. + will disable the default server certificate verification. **ssl_context** can be used to enable verification. exception pan.xapi.PanXapiError diff --git a/doc/panwfapi.rst b/doc/panwfapi.rst index 7ab9a65..c09c4e2 100644 --- a/doc/panwfapi.rst +++ b/doc/panwfapi.rst @@ -251,9 +251,6 @@ DESCRIPTION This is the default. - SSL server certificate verification is only performed in Python - version 2.7.9 and 3.4.3 and greater. - ``--ssl`` is ignored if ``--cafile`` or ``--capath`` are specified. ``--cafile`` *path* diff --git a/doc/panxapi.rst b/doc/panxapi.rst index 25cfa59..355274c 100644 --- a/doc/panxapi.rst +++ b/doc/panxapi.rst @@ -504,15 +504,13 @@ DESCRIPTION Specify the ``cafile`` value for HTTPS requests. ``cafile`` is a file containing CA certificates to be used for SSL server certificate verification. By default the SSL server certificate is - not verified. ``--cafile`` is supported starting in Python versions - 2.7.9 and 3.2. + not verified. ``--capath`` *path* Specify the ``capath`` value for HTTPS requests. ``capath`` is a directory of hashed certificate files to be used for SSL server certificate verification. By default the SSL server certificate is - not verified. ``--cafile`` is supported starting in Python versions - 2.7.9 and 3.2. + not verified. ``--version`` Display version. diff --git a/lib/pan/commit.py b/lib/pan/commit.py index c505fe6..00ca3f8 100644 --- a/lib/pan/commit.py +++ b/lib/pan/commit.py @@ -14,7 +14,6 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -from __future__ import print_function import sys import logging diff --git a/lib/pan/http.py b/lib/pan/http.py index 29270a6..4bb74af 100644 --- a/lib/pan/http.py +++ b/lib/pan/http.py @@ -26,28 +26,10 @@ import requests _using_requests = True except ImportError: - try: - # 3.2 - from urllib.request import Request, \ - build_opener, HTTPErrorProcessor, HTTPSHandler - from urllib.error import URLError - from urllib.parse import urlencode - except ImportError: - # 2.7 - from urllib2 import Request, URLError, \ - build_opener, HTTPErrorProcessor, HTTPSHandler - from urllib import urlencode - - -def _isunicode(s): - try: - if isinstance(s, unicode): - return True - return False - except NameError: - if isinstance(s, str): - return True - return False + from urllib.request import Request, \ + build_opener, HTTPErrorProcessor, HTTPSHandler + from urllib.error import URLError + from urllib.parse import urlencode class PanHttpError(Exception): @@ -119,8 +101,8 @@ def _http_request_urllib(self, url, headers, data, params): x = set(k.lower() for k in headers) if not 'content-type' in x: kwargs['data'] = urlencode(data) - # data must by type 'bytes' for 3.x - if _isunicode(kwargs['data']): + # data must be type 'bytes' + if isinstance(kwargs['data'], str): kwargs['data'] = kwargs['data'].encode() request = Request(**kwargs) @@ -131,11 +113,7 @@ def _http_request_urllib(self, url, headers, data, params): if self.timeout is not None: kwargs['timeout'] = self.timeout - if not self.verify_cert and \ - (sys.version_info.major == 2 and - sys.hexversion >= 0x02070900 or - sys.version_info.major == 3 and - sys.hexversion >= 0x03040300): + if not self.verify_cert: context = ssl._create_unverified_context() kwargs['context'] = context @@ -147,12 +125,7 @@ def _http_request_urllib(self, url, headers, data, params): raise PanHttpError(str(e)) self.code = response.getcode() - if hasattr(response, 'reason'): - # 3.2 - self.reason = response.reason - elif hasattr(response, 'msg'): - # 2.7 - self.reason = response.msg + self.reason = response.reason try: self.headers = email.message_from_string(str(response.info())) diff --git a/lib/pan/licapi/v1.py b/lib/pan/licapi/v1.py index 87150dd..09150a8 100644 --- a/lib/pan/licapi/v1.py +++ b/lib/pan/licapi/v1.py @@ -173,7 +173,7 @@ def _api_request(self, url, headers, data=None, params=None): try: self.http.http_request(url=url, - headers=self.headers, + headers=headers, data=data, params=params) except pan.http.PanHttpError as e: diff --git a/lib/pan/rc.py b/lib/pan/rc.py index 58f4dae..f86249f 100644 --- a/lib/pan/rc.py +++ b/lib/pan/rc.py @@ -14,7 +14,6 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -from __future__ import print_function import sys import os import re diff --git a/lib/pan/wfapi.py b/lib/pan/wfapi.py index 8ab61a3..f6540db 100644 --- a/lib/pan/wfapi.py +++ b/lib/pan/wfapi.py @@ -27,7 +27,6 @@ # decided to not require non-default modules. That decision may need # to be revisited as some parts of this are not clean. -from __future__ import print_function import socket import sys import os @@ -36,21 +35,12 @@ import email.errors import email.utils import logging -try: - # 3.2 - from urllib.request import Request, \ - build_opener, HTTPErrorProcessor, HTTPSHandler - from urllib.error import URLError - from urllib.parse import urlencode - from http.client import responses - _legacy_urllib = False -except ImportError: - # 2.7 - from urllib2 import Request, URLError, \ - build_opener, HTTPErrorProcessor, HTTPSHandler - from urllib import urlencode - from httplib import responses - _legacy_urllib = True + +from urllib.request import Request, \ + build_opener, HTTPErrorProcessor, HTTPSHandler +from urllib.error import URLError +from urllib.parse import urlencode +from http.client import responses import xml.etree.ElementTree as etree from . import __version__, DEBUG1, DEBUG2, DEBUG3 @@ -95,28 +85,6 @@ } -def _isunicode(s): - try: - if isinstance(s, unicode): - return True - return False - except NameError: - if isinstance(s, str): - return True - return False - - -def _isbytes(s): - try: - if isinstance(s, basestring) and isinstance(s, bytes): - return True - return False - except NameError: - if isinstance(s, bytes): - return True - return False - - class PanWFapiError(Exception): pass @@ -190,9 +158,6 @@ def __init__(self, else: self.uri = 'https://%s' % self.hostname - if _legacy_urllib: - self._log(DEBUG2, 'using legacy urllib') - def __str__(self): x = self.__dict__.copy() for k in x: @@ -320,23 +285,12 @@ def xml_root(self): self._log(DEBUG3, 'xml_root.decode(): %s', type(s.decode(_encoding))) return s.decode(_encoding) -# XXX Unicode notes -# 2.7 -# decode() str (bytes) -> unicode -# encode() unicode -> str (bytes) -# encode() of str will call decode() -# 3.x -# decode() bytes -> str (unicode) -# encode() str (unicode) -> bytes -# cannot encode() bytes -# cannot decode() str - def __api_request(self, request_uri, body, headers={}): url = self.uri url += request_uri - # body must by type 'bytes' for 3.x - if _isunicode(body): + # body must be type 'bytes' + if isinstance(body, str): body = body.encode() request = Request(url, body, headers) @@ -845,10 +799,9 @@ def _encode_field(self, name, value): s = '%s="%s"' % (name, value) self._log(DEBUG1, '_FormDataPart._encode_field: %s %s', type(s), s) - if _isunicode(s): - s = s.encode('utf-8') - self._log(DEBUG1, '_FormDataPart._encode_field: %s %s', - type(s), s) + s = s.encode('utf-8') + self._log(DEBUG1, '_FormDataPart._encode_field: %s %s', + type(s), s) return s if not [ch for ch in '\r\n\\' if ch in value]: @@ -863,7 +816,7 @@ def _encode_field(self, name, value): return ('%s*=%s' % (name, value)).encode('ascii') def add_body(self, body): - if _isunicode(body): + if isinstance(body, str): body = body.encode('latin-1') self.body = body self._log(DEBUG1, '_FormDataPart.add_body: %s %d', diff --git a/lib/pan/xapi.py b/lib/pan/xapi.py index 1318f98..a05dcb4 100644 --- a/lib/pan/xapi.py +++ b/lib/pan/xapi.py @@ -21,7 +21,6 @@ Firewalls. """ -from __future__ import print_function import sys import re import time @@ -31,23 +30,9 @@ except ImportError: raise ValueError('SSL support not available') -try: - # 3.2 - from urllib.request import Request, urlopen, \ - build_opener, install_opener, HTTPSHandler - from urllib.error import URLError - from urllib.parse import urlencode - _legacy_urllib = False -except ImportError: - # 2.7 - from urllib2 import Request, urlopen, URLError, \ - build_opener, install_opener - try: - from urllib2 import HTTPSHandler - except: - pass - from urllib import urlencode - _legacy_urllib = True +from urllib.request import Request, urlopen +from urllib.error import URLError +from urllib.parse import urlencode import xml.etree.ElementTree as etree @@ -91,8 +76,7 @@ def __init__(self, self._log(DEBUG3, 'Python version: %s', sys.version) self._log(DEBUG3, 'xml.etree.ElementTree version: %s', etree.VERSION) - if hasattr(ssl, 'OPENSSL_VERSION'): # added 2.7 - self._log(DEBUG3, 'ssl: %s', ssl.OPENSSL_VERSION) + self._log(DEBUG3, 'ssl: %s', ssl.OPENSSL_VERSION) self._log(DEBUG3, 'pan-python version: %s', __version__) if self.port is not None: @@ -111,18 +95,6 @@ def __init__(self, except ValueError: raise PanXapiError('Invalid timeout: %s' % self.timeout) - if self.ssl_context is not None: - try: - ssl.SSLContext(ssl.PROTOCOL_SSLv23) - except AttributeError: - raise PanXapiError('SSL module has no SSLContext()') - - # handle Python versions with no ssl.CertificateError - if hasattr(ssl, 'CertificateError'): - self._certificateerror = ssl.CertificateError - else: - self._certificateerror = NotImplementedError # XXX Can't happen - init_panrc = {} # .panrc args from constructor if api_username is not None: init_panrc['api_username'] = api_username @@ -192,9 +164,6 @@ def __init__(self, # _legacy_api is used for PAN-OS < 4.1.0 self.uri += '/api/' if not self._legacy_api else '/esp/restapi.esp' - if _legacy_urllib: - self._log(DEBUG2, 'using legacy urllib') - def __str__(self): x = self.__dict__.copy() for k in x: @@ -215,20 +184,10 @@ def __clear_response(self): self.export_result = None def __get_header(self, response, name): - """use getheader() method depending or urllib in use""" - s = None types = set() - if hasattr(response, 'getheader'): - # 3.2, http.client.HTTPResponse - s = response.getheader(name) - elif hasattr(response.info(), 'getheader'): - # 2.7, httplib.HTTPResponse - s = response.info().getheader(name) - else: - raise PanXapiError('no getheader() method found in ' + - 'urllib response') + s = response.getheader(name) if s is not None: types = [type.lower() for type in s.split(';')] @@ -512,7 +471,7 @@ def __api_request(self, query): url += '?' + data request = Request(url) else: - # data must by type 'bytes' for 3.x + # data must be type 'bytes' request = Request(url, data.encode()) self._log(DEBUG1, 'method: %s', request.get_method()) @@ -521,22 +480,12 @@ def __api_request(self, query): 'url': request, } - if (sys.hexversion & 0xffff0000 == 0x02060000): - # XXX allow 2.6 as a one-off while still using .major - # named attribute - pass - elif (sys.version_info.major == 2 and sys.hexversion >= 0x02070900 or - sys.version_info.major == 3 and sys.hexversion >= 0x03040300): - # see PEP 476; urlopen() has context - if self.ssl_context is None: - # don't perform certificate verification - kwargs['context'] = ssl._create_unverified_context() - else: - kwargs['context'] = self.ssl_context - elif self.ssl_context is not None: - https_handler = HTTPSHandler(context=self.ssl_context) - opener = build_opener(https_handler) - install_opener(opener) + # see PEP 476; urlopen() has context + if self.ssl_context is None: + # don't perform certificate verification + kwargs['context'] = ssl._create_unverified_context() + else: + kwargs['context'] = self.ssl_context if self.timeout is not None: kwargs['timeout'] = self.timeout @@ -545,7 +494,7 @@ def __api_request(self, query): response = urlopen(**kwargs) # XXX handle httplib.BadStatusLine when http to port 443 - except self._certificateerror as e: + except ssl.CertificateError as e: self.status_detail = 'ssl.CertificateError: %s' % e return False except URLError as error: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9787c3b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 2a9acf1..fdccb10 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,29 @@ -[bdist_wheel] -universal = 1 +[metadata] +name = pan-python +version = attr: pan.__version__ +author = Kevin Steves +author_email = kevin.steves@pobox.com +description = Multi-tool set for Palo Alto Networks PAN-OS, Panorama, WildFire and AutoFocus +long_description = file: README.rst +long_description_content_type = text/x-rst +url = https://github.com/kevinsteves/pan-python +license = ISC +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: ISC License (ISCL) + +[options] +package_dir = + =lib +packages = + pan + pan/afapi + pan/licapi +scripts = + bin/panxapi.py + bin/panconf.py + bin/panwfapi.py + bin/panafapi.py + bin/panlicapi.py +install_requires = +python_requires = >=3.7 diff --git a/setup.py b/setup.py deleted file mode 100755 index 60f335f..0000000 --- a/setup.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python - -# ./setup.py sdist bdist_wheel - -from setuptools import setup -import sys -sys.path[:0] = ['lib'] -from pan.xapi import __version__ - -with open('README.rst') as f: - long_description = f.read() - -setup( - name='pan-python', - version=__version__, - author='Kevin Steves', - author_email='kevin.steves@pobox.com', - description='Multi-tool set for Palo Alto Networks' + - ' PAN-OS, Panorama, WildFire and AutoFocus', - long_description=long_description, - long_description_content_type='text/x-rst', - url='https://github.com/kevinsteves/pan-python', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: End Users/Desktop', - 'License :: OSI Approved :: ISC License (ISCL)', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - ], - package_dir={'': 'lib'}, - packages=[ - 'pan', - 'pan/afapi', - 'pan/licapi' - ], - scripts=[ - 'bin/panxapi.py', - 'bin/panconf.py', - 'bin/panwfapi.py', - 'bin/panafapi.py', - 'bin/panlicapi.py', - ], -)