ifkit 0.1.51__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ifkit-0.1.51/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 infuq
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ include README.md LICENSE
ifkit-0.1.51/PKG-INFO ADDED
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.1
2
+ Name: ifkit
3
+ Version: 0.1.51
4
+ Summary: 日常工作使用的小工具/小功能
5
+ Home-page: https://www.infuq.com
6
+ Author: infuq
7
+ Author-email: 864511259@qq.com
8
+ License: MIT
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: Implementation :: CPython
14
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
15
+ Requires-Python: >=3.6.0
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+
19
+
20
+ 日常工作使用的小工具/小功能
21
+
22
+
23
+ [个人站点](https://www.infuq.com)
ifkit-0.1.51/README.md ADDED
@@ -0,0 +1,4 @@
1
+ 日常工作使用的小工具/小功能
2
+
3
+
4
+ [个人站点](https://www.infuq.com)
@@ -0,0 +1,9 @@
1
+ from .csv import *
2
+ from .excel import ReadExcelUtil, WriteXlsxUtil
3
+ from .json import *
4
+ from .mq import *
5
+ from .qyweixin import *
6
+ from .sms import *
7
+ from .txt import *
8
+ from .mysql_export_excel import export_excel
9
+
@@ -0,0 +1,8 @@
1
+ # 8b d8 Yb dP 88""Yb db dP""b8 88 dP db dP""b8 888888
2
+ # 88b d88 YbdP 88__dP dPYb dP `" 88odP dPYb dP `" 88__
3
+ # 88YbdP88 8P 88""" dP__Yb Yb 88"Yb dP__Yb Yb "88 88""
4
+ # 88 YY 88 dP 88 dP""""Yb YboodP 88 Yb dP""""Yb YboodP 888888
5
+
6
+ VERSION = (5, 2, 0)
7
+
8
+ __version__ = '.'.join(map(str, VERSION))
@@ -0,0 +1,18 @@
1
+ import csv
2
+ from collections.abc import Iterable
3
+ from collections.abc import Iterator
4
+
5
+ # 读取 CSV 文件
6
+ def read_csv(file_name, encoding='UTF8', start_read_row_num=0):
7
+ row_num = 0
8
+ with open(file_name, 'r', encoding=encoding) as f: # encoding='UTF8' encoding='GBK'
9
+ reader = csv.DictReader(f)
10
+ for row_data in reader:
11
+ if row_num < start_read_row_num:
12
+ row_num = row_num + 1
13
+ continue
14
+
15
+ rn = row_num
16
+ row_num = row_num + 1
17
+ yield (rn, row_data)
18
+
@@ -0,0 +1,173 @@
1
+ """
2
+ 读取xlsx或xls文件
3
+
4
+ pip install xlrd
5
+ pip install xlwt
6
+ pip install openpyxl
7
+ """
8
+
9
+ import openpyxl
10
+ import os
11
+ import xlrd
12
+ import xlwt
13
+ from collections.abc import Iterable
14
+ from collections.abc import Iterator
15
+ import datetime
16
+ import re
17
+
18
+
19
+ # 读取文件
20
+ class ReadExcelUtil(Iterable):
21
+
22
+ def __init__(self, file_name, start_read_row_num=0):
23
+ self.filename = file_name
24
+ self.suffix = os.path.splitext(self.filename)[-1][1:].lower() # xls xlsx
25
+ self.start_read_row_num = start_read_row_num # 开始从第N行读取数据
26
+
27
+
28
+ if self.suffix == 'xlsx':
29
+ self.workbook = openpyxl.load_workbook(self.filename, data_only=True)
30
+ self.sheetnames = self.workbook.sheetnames # Sheet1 Sheet2 Sheet3...
31
+ self.sheet = self.workbook[self.sheetnames[0]] # 根据名称默认读取第一个Sheet
32
+
33
+ if self.suffix == 'xls':
34
+ self.workbook = xlrd.open_workbook(self.filename, encoding_override="UTF-8")
35
+ self.sheetnames = self.workbook._sheet_names # Sheet1 Sheet2 Sheet3...
36
+ # self.sheet = self.workbook[0] # 根据下标默认读取第一个Sheet
37
+ self.sheet = self.workbook[self.sheetnames[0]] # 根据名称默认读取第一个Sheet
38
+
39
+ def __iter__(self):
40
+ if self.suffix == 'xlsx':
41
+ return ReadXlsxIterator(self.sheet, self.start_read_row_num)
42
+ if self.suffix == 'xls':
43
+ return ReadXlsIterator(self.sheet, self.start_read_row_num)
44
+
45
+
46
+ # 迭代器 xlsx
47
+ class ReadXlsxIterator(Iterator):
48
+
49
+ def __init__(self, sheet, start_read_row_num):
50
+ self.sheet = sheet
51
+ self.max_row = sheet.max_row
52
+ self.max_column = sheet.max_column
53
+ self.row_num = start_read_row_num
54
+
55
+ def __next__(self):
56
+ try:
57
+ if self.row_num <= self.max_row:
58
+ # 读取行内容
59
+ row_data = []
60
+ for i in range(1, self.max_column + 1):
61
+ cell_value = self.sheet.cell(row=self.row_num, column=i).value
62
+ row_data.append(cell_value)
63
+
64
+ rn = self.row_num
65
+ self.row_num += 1
66
+ # (行号, 行内容)
67
+ return (rn, row_data)
68
+ except Exception:
69
+ raise StopIteration
70
+ raise StopIteration
71
+
72
+
73
+ # 迭代器 xls
74
+ class ReadXlsIterator(Iterator):
75
+
76
+ def __init__(self, sheet, start_read_row_num):
77
+ self.sheet = sheet
78
+ self.nrows = sheet.nrows
79
+ self.row_num = start_read_row_num
80
+
81
+ def __next__(self):
82
+ try:
83
+ if self.row_num <= self.nrows:
84
+ # 读取行内容
85
+ # (行号, 行内容)
86
+ rn = self.row_num
87
+ self.row_num += 1
88
+ return (rn, self.sheet.row_values(rn))
89
+ except Exception:
90
+ raise StopIteration
91
+ raise StopIteration
92
+
93
+
94
+ # 写入文件
95
+ class WriteXlsxUtil(object):
96
+
97
+ def __init__(self, file_name):
98
+ self.index = 0
99
+ self.suffix = '.xlsx'
100
+ self.workbook = openpyxl.Workbook()
101
+ self.sheet = self.workbook.create_sheet(index=self.index, title="Sheet")
102
+ self.empty = True # 标记是否真的写入了内容
103
+ self.row = 0
104
+ if file_name is None:
105
+ self.file_name = "File-" + datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') + self.suffix
106
+ else:
107
+ self.file_name = file_name + self.suffix
108
+
109
+
110
+ def set_sheet(self, sheet_name):
111
+ self.index = self.index + 1
112
+ self.sheet = self.workbook.create_sheet(index=self.index, title=sheet_name)
113
+ self.row = 0
114
+
115
+ # 设置列宽
116
+ def column_width(self, index, width):
117
+ self.sheet.column_dimensions[index].width = width
118
+
119
+ # 写入单元格内容
120
+ def write(self, content):
121
+ self.empty = False
122
+ self.row += 1
123
+ for index in range(1, len(content) + 1): # index = column
124
+ if isinstance(content[index - 1], str) or isinstance(content[index - 1], (int, float)):
125
+ v = str(content[index - 1])
126
+ digit = re.match(r'^\d+(\.\d+)?$', v, re.I)
127
+ if digit:
128
+ self.sheet.cell(self.row, index).value = v
129
+ else:
130
+ self.sheet.cell(self.row, index).value = v
131
+ else:
132
+ cell = content[index - 1]
133
+ self.sheet.cell(self.row, index).value = cell['value']
134
+ if 'color' in cell:
135
+ self.sheet.cell(self.row, index).font = openpyxl.styles.Font(color=cell['color'])
136
+
137
+ # 保存文件
138
+ def save(self):
139
+ if not self.empty:
140
+ self.workbook.save("./" + self.file_name)
141
+
142
+
143
+
144
+ # class WriteXlsUtil(object):
145
+ #
146
+ # def __init__(self, file_name='File'):
147
+ # self.suffix = '.xls'
148
+ # self.workbook = xlwt.Workbook()
149
+ # self.sheet = self.workbook.add_sheet("Sheet")
150
+ # self.file_name = file_name
151
+ # self.empty = True
152
+ # self.row = -1
153
+ #
154
+ # # 写入单元格内容
155
+ # def write(self, content):
156
+ # self.empty = False
157
+ # self.row += 1
158
+ # for index in range(len(content)):
159
+ # cnt = str(content[index])
160
+ # if cnt.isdigit():
161
+ # self.sheet.write(self.row, index, cnt.zfill(len(cnt)))
162
+ # else:
163
+ # self.sheet.write(self.row, index, content[index])
164
+ #
165
+ # # 保存文件
166
+ # def save(self):
167
+ # if not self.empty:
168
+ # self.workbook.save("./" + self.file_name + "-" + datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') + self.suffix)
169
+
170
+
171
+
172
+
173
+ __all__ = ['ReadExcelUtil', 'WriteXlsxUtil']
@@ -0,0 +1,11 @@
1
+ from datetime import date, datetime
2
+ import json
3
+
4
+ class ComplexEncoder(json.JSONEncoder):
5
+ def default(self, obj):
6
+ if isinstance(obj, datetime):
7
+ return obj.strftime('%Y-%m-%d %H:%M:%S')
8
+ elif isinstance(obj, date):
9
+ return obj.strftime('%Y-%m-%d')
10
+ else:
11
+ return json.JSONEncoder.default(self, obj)
@@ -0,0 +1,62 @@
1
+ """
2
+ pip install mq_http_sdk
3
+ """
4
+
5
+ import sys
6
+ import time
7
+ from mq_http_sdk.mq_exception import MQExceptionBase
8
+ from mq_http_sdk.mq_producer import *
9
+ from mq_http_sdk.mq_client import *
10
+
11
+
12
+ # 生产者通过HTTP协议发送消息
13
+ def send_http(host, access_id, access_key, instance_id, topic_name, message_body, message_tag, message_key):
14
+ mq_client = MQClient(host=host, access_id=access_id, access_key=access_key)
15
+
16
+ producer = mq_client.get_producer(instance_id, topic_name)
17
+
18
+ try:
19
+ msg = TopicMessage(message_body, message_tag)
20
+ msg.put_property("__key", "__value") # 设置属性
21
+ msg.set_message_key(message_key)
22
+ # msg.set_sharding_key("")
23
+
24
+ re_msg = producer.publish_message(msg)
25
+ return re_msg.message_id
26
+ except MQExceptionBase as e:
27
+ if e.type == "TopicNotExist":
28
+ print("Topic not exist, please create it.")
29
+ print("Publish Message Fail. Exception:%s" % e)
30
+
31
+ return None
32
+
33
+
34
+ # 消费者通过HTTP协议消费消息
35
+ def consume_loop_http(host, access_id, access_key, instance_id, topic_name, group_id, callback):
36
+
37
+ mq_client = MQClient(host=host, access_id=access_id, access_key=access_key)
38
+ consumer = mq_client.get_consumer(instance_id, topic_name, group_id)
39
+ wait_seconds = 3
40
+ batch = 3
41
+ while True:
42
+ try:
43
+ # recv_msgs = consumer.consume_message_orderly(batch, wait_seconds)
44
+ recv_msgs = consumer.consume_message(batch, wait_seconds)
45
+ for message in recv_msgs:
46
+ # 回调业务方法
47
+ callback(message)
48
+ consumer.ack_message(message.receipt_handle.split())
49
+
50
+ except MQExceptionBase as e:
51
+ if e.type == "MessageNotExist":
52
+ # print(("No new message! RequestId: %s" % e.req_id))
53
+ # continue
54
+ # break
55
+ time.sleep(5)
56
+ continue
57
+
58
+ print(("Consume Message Fail! Exception:%s\n" % e))
59
+ time.sleep(2)
60
+ continue
61
+
62
+
@@ -0,0 +1,31 @@
1
+ #! /usr/bin/env python
2
+
3
+ import pymysql
4
+
5
+
6
+ def _find_all_tables(cursor, table_schema):
7
+ sql = "SELECT * FROM information_schema.tables WHERE table_schema = '%s';"
8
+ sql = sql % (table_schema,)
9
+ cursor.execute(sql)
10
+ all_data = cursor.fetchall()
11
+ all_tables = []
12
+ for data in all_data:
13
+ all_tables.append(data['TABLE_NAME'])
14
+ return all_tables
15
+
16
+
17
+ def find_all_tables(host, port, user, passwd, db):
18
+ conn = pymysql.connect(host=host,
19
+ port=port,
20
+ user=user,
21
+ passwd=passwd,
22
+ db=db)
23
+ cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
24
+
25
+ all_tables = _find_all_tables(cursor, db)
26
+
27
+ cursor.close()
28
+ conn.close()
29
+
30
+ return all_tables
31
+
@@ -0,0 +1,14 @@
1
+ #! /usr/bin/env python
2
+
3
+ def show_create_table(cursor, table_name):
4
+ try:
5
+ sql = "show create table %s"
6
+ sql = sql % (table_name,)
7
+ cursor.execute(sql)
8
+ all_data = cursor.fetchall()
9
+ return all_data[0]['Create Table']
10
+ except Exception as e:
11
+ print(e)
12
+
13
+
14
+
@@ -0,0 +1,63 @@
1
+ #! /usr/bin/env python
2
+
3
+ import decimal
4
+ import re
5
+
6
+ import pymysql
7
+
8
+
9
+ def export_excel(host, port, user, passwd, db, query_sql, file_name=None):
10
+ conn = pymysql.connect(host=host,
11
+ port=port,
12
+ user=user,
13
+ passwd=passwd,
14
+ db=db)
15
+ cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
16
+
17
+ from .excel import WriteXlsxUtil
18
+ write_book = WriteXlsxUtil(file_name)
19
+
20
+ cursor.execute(query_sql)
21
+ alias_field = alias(cursor.description)
22
+ write_book.write(tuple(alias_field))
23
+
24
+ all_data = cursor.fetchall()
25
+ for data in all_data:
26
+ data_list = []
27
+ for field in alias_field:
28
+ value = data[field]
29
+ data_list.append(replace(value))
30
+ write_book.write(tuple(data_list))
31
+
32
+ # 保存文件
33
+ write_book.save()
34
+
35
+ # 关闭数据库连接
36
+ cursor.close()
37
+ conn.close()
38
+
39
+
40
+ def alias(description):
41
+ alias_field = []
42
+ for i in range(len(description)):
43
+ alias_field.append(description[i][0])
44
+ return alias_field
45
+
46
+ def replace(data):
47
+ if data:
48
+ if isinstance(data, decimal.Decimal):
49
+ if data == 0:
50
+ return 0
51
+ else:
52
+ return replace(str(data))
53
+ elif isinstance(data, str):
54
+ # 70.10 -> 70.1
55
+ data = re.sub(r'^(.*\.\d+?)0+$', r'\1', data)
56
+ # 70.00 -> 70
57
+ data = re.sub(r'^(.*)\.0+$', r'\1', data)
58
+
59
+ return data
60
+ else:
61
+ return replace(str(data))
62
+ else:
63
+ return ''
@@ -0,0 +1,13 @@
1
+ import requests
2
+
3
+ def send_markdown_message(content, key):
4
+ url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s'
5
+ url = url % (key,)
6
+ payload = {
7
+ 'msgtype': 'markdown',
8
+ 'markdown': {
9
+ 'content': content
10
+ }
11
+ }
12
+ response = requests.post(url, json=payload)
13
+ return response.json()
@@ -0,0 +1,21 @@
1
+ import requests
2
+
3
+ # 发送短信
4
+ def send_sms(content, accesskey, secret, sign, templateId, mobile):
5
+
6
+ headers = {
7
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
8
+ }
9
+
10
+ data = {
11
+ 'accesskey': accesskey,
12
+ 'secret': secret,
13
+ 'sign': sign,
14
+ 'templateId': templateId,
15
+ 'mobile': mobile,
16
+ 'content': content
17
+ }
18
+
19
+ # 发送
20
+ response = requests.post('http://api.1cloudsp.com/api/v2/single_send', data=data, headers=headers)
21
+ return response.text
@@ -0,0 +1,13 @@
1
+
2
+ # 读取 txt 文件
3
+ def read_txt(file_name, encoding='GBK', start_read_row_num=0):
4
+ row_num = 0
5
+ with open(file_name, 'r', encoding=encoding) as f:
6
+ for row_data in f.readlines():
7
+ if row_num < start_read_row_num:
8
+ row_num = row_num + 1
9
+ continue
10
+
11
+ rn = row_num
12
+ row_num = row_num + 1
13
+ yield (rn, row_data)
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.1
2
+ Name: ifkit
3
+ Version: 0.1.51
4
+ Summary: 日常工作使用的小工具/小功能
5
+ Home-page: https://www.infuq.com
6
+ Author: infuq
7
+ Author-email: 864511259@qq.com
8
+ License: MIT
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: Implementation :: CPython
14
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
15
+ Requires-Python: >=3.6.0
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+
19
+
20
+ 日常工作使用的小工具/小功能
21
+
22
+
23
+ [个人站点](https://www.infuq.com)
@@ -0,0 +1,20 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ setup.py
5
+ ifkit/__init__.py
6
+ ifkit/__version__.py
7
+ ifkit/csv.py
8
+ ifkit/excel.py
9
+ ifkit/json.py
10
+ ifkit/mq.py
11
+ ifkit/mysql_all_tables.py
12
+ ifkit/mysql_create_table_statement.py
13
+ ifkit/mysql_export_excel.py
14
+ ifkit/qyweixin.py
15
+ ifkit/sms.py
16
+ ifkit/txt.py
17
+ ifkit.egg-info/PKG-INFO
18
+ ifkit.egg-info/SOURCES.txt
19
+ ifkit.egg-info/dependency_links.txt
20
+ ifkit.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ ifkit
ifkit-0.1.51/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
ifkit-0.1.51/setup.py ADDED
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # Note: To use the 'upload' functionality of this file, you must:
5
+ # $ pipenv install twine --dev
6
+
7
+ import io
8
+ import os
9
+ import sys
10
+ from shutil import rmtree
11
+
12
+ from setuptools import find_packages, setup, Command
13
+
14
+ # Package meta-data.
15
+ NAME = 'ifkit'
16
+ DESCRIPTION = '日常工作使用的小工具/小功能'
17
+ URL = 'https://www.infuq.com'
18
+ EMAIL = '864511259@qq.com'
19
+ AUTHOR = 'infuq'
20
+ REQUIRES_PYTHON = '>=3.6.0'
21
+ VERSION = '0.1.51'
22
+
23
+ # What packages are required for this module to be executed?
24
+ REQUIRED = [
25
+ # 'requests', 'maya', 'records',
26
+ ]
27
+
28
+ # What packages are optional?
29
+ EXTRAS = {
30
+ # 'fancy feature': ['django'],
31
+ }
32
+
33
+ # The rest you shouldn't have to touch too much :)
34
+ # ------------------------------------------------
35
+ # Except, perhaps the License and Trove Classifiers!
36
+ # If you do change the License, remember to change the Trove Classifier for that!
37
+
38
+ here = os.path.abspath(os.path.dirname(__file__))
39
+
40
+ # Import the README and use it as the long-description.
41
+ # Note: this will only work if 'README.md' is present in your MANIFEST.in file!
42
+ try:
43
+ with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
44
+ long_description = '\n' + f.read()
45
+ except FileNotFoundError:
46
+ long_description = DESCRIPTION
47
+
48
+ # Load the package's __version__.py module as a dictionary.
49
+ about = {}
50
+ if not VERSION:
51
+ project_slug = NAME.lower().replace("-", "_").replace(" ", "_")
52
+ with open(os.path.join(here, project_slug, '__version__.py')) as f:
53
+ exec(f.read(), about)
54
+ else:
55
+ about['__version__'] = VERSION
56
+
57
+
58
+ class UploadCommand(Command):
59
+ """Support setup.py upload."""
60
+
61
+ description = 'Build and publish the package.'
62
+ user_options = []
63
+
64
+ @staticmethod
65
+ def status(s):
66
+ """Prints things in bold."""
67
+ print('\033[1m{0}\033[0m'.format(s))
68
+
69
+ def initialize_options(self):
70
+ pass
71
+
72
+ def finalize_options(self):
73
+ pass
74
+
75
+ def run(self):
76
+ try:
77
+ self.status('Removing previous builds…')
78
+ rmtree(os.path.join(here, 'dist'))
79
+ except OSError:
80
+ pass
81
+
82
+ self.status('Building Source and Wheel (universal) distribution…')
83
+ os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))
84
+
85
+ self.status('Uploading the package to PyPI via Twine…')
86
+ os.system('twine upload dist/*')
87
+
88
+ self.status('Pushing git tags…')
89
+ os.system('git tag v{0}'.format(about['__version__']))
90
+ os.system('git push --tags')
91
+
92
+ sys.exit()
93
+
94
+
95
+ # Where the magic happens:
96
+ setup(
97
+ name=NAME,
98
+ version=about['__version__'],
99
+ description=DESCRIPTION,
100
+ long_description=long_description,
101
+ long_description_content_type='text/markdown',
102
+ author=AUTHOR,
103
+ author_email=EMAIL,
104
+ python_requires=REQUIRES_PYTHON,
105
+ url=URL,
106
+ packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]),
107
+ # If your package is a single module, use this instead of 'packages':
108
+ # py_modules=['mypackage'],
109
+
110
+ # entry_points={
111
+ # 'console_scripts': ['mycli=mymodule:cli'],
112
+ # },
113
+ install_requires=REQUIRED,
114
+ extras_require=EXTRAS,
115
+ include_package_data=True,
116
+ license='MIT',
117
+ classifiers=[
118
+ # Trove classifiers
119
+ # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
120
+ 'License :: OSI Approved :: MIT License',
121
+ 'Programming Language :: Python',
122
+ 'Programming Language :: Python :: 3',
123
+ 'Programming Language :: Python :: 3.6',
124
+ 'Programming Language :: Python :: Implementation :: CPython',
125
+ 'Programming Language :: Python :: Implementation :: PyPy'
126
+ ],
127
+ # $ setup.py publish support.
128
+ cmdclass={
129
+ 'upload': UploadCommand,
130
+ },
131
+ )