mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-09 17:07:43 +01:00
"mqtt added"
This commit is contained in:
parent
9c3a1f564b
commit
12857c73ef
50 changed files with 10834 additions and 113 deletions
|
@ -1 +1 @@
|
|||
__version__ = "4.0.0.31"
|
||||
__version__ = "4.0.0.32"
|
|
@ -60,7 +60,7 @@ class Actor:
|
|||
def __str__(self):
|
||||
return "name={} props={}, state={}, type={}".format(self.name, self.props, self.state, self.type)
|
||||
def to_dict(self):
|
||||
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state())
|
||||
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state2="HELLO WORLD", state=self.instance.get_state())
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -150,3 +150,12 @@ class NotificationAction:
|
|||
|
||||
def to_dict(self):
|
||||
return dict(id=self.id, label=self.label)
|
||||
|
||||
class NotificationType(Enum):
|
||||
INFO="info"
|
||||
WARNING="warning"
|
||||
ERROR="error"
|
||||
SUCCESS="success"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
|
@ -35,6 +35,7 @@ class CBPiSensor(CBPiBase, metaclass=ABCMeta):
|
|||
def push_update(self, value):
|
||||
try:
|
||||
self.cbpi.ws.send(dict(topic="sensorstate", id=self.id, value=value))
|
||||
self.cbpi.push_update("cbpi/sensor/{}/udpate".format(self.id), dict(id=self.id, value=value), retain=True)
|
||||
except:
|
||||
logging.error("Faild to push sensor update")
|
||||
|
||||
|
|
|
@ -6,6 +6,12 @@ index_url: /cbpi_ui/static/index.html
|
|||
|
||||
port: 8000
|
||||
|
||||
mqtt: false
|
||||
mqtt_host: localhost
|
||||
mqtt_port: 1883
|
||||
mqtt_username: ""
|
||||
mqtt_password: ""
|
||||
|
||||
username: cbpi
|
||||
password: 123
|
||||
|
||||
|
|
|
@ -8,15 +8,14 @@ class ActorController(BasicController):
|
|||
super(ActorController, self).__init__(cbpi, Actor,"actor.json")
|
||||
self.update_key = "actorupdate"
|
||||
|
||||
|
||||
async def on(self, id):
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
|
||||
if item.instance.state is False:
|
||||
await item.instance.on()
|
||||
await self.push_udpate()
|
||||
#await self.cbpi.satellite.publish("cbpi/actor/on", "ACTOR ON")
|
||||
self.cbpi.push_update("cbpi/actor/"+id, item.to_dict(), True)
|
||||
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on Actor {} {}".format(id, e))
|
||||
|
||||
|
@ -26,14 +25,16 @@ class ActorController(BasicController):
|
|||
if item.instance.state is True:
|
||||
await item.instance.off()
|
||||
await self.push_udpate()
|
||||
self.cbpi.push_update("cbpi/actor/"+id, item.to_dict())
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on Actor {} {}".format(id, e))
|
||||
logging.error("Faild to switch on Actor {} {}".format(id, e), True)
|
||||
|
||||
async def toogle(self, id):
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
await instance.toggle()
|
||||
self.cbpi.push_update("cbpi/actor/update", item.to_dict())
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on Actor {} {}".format(id, e))
|
||||
|
|
@ -30,7 +30,6 @@ class BasicController:
|
|||
await self.load()
|
||||
|
||||
def create(self, data):
|
||||
|
||||
return self.resource(data.get("id"), data.get("name"), type=data.get("type"), props=Props(data.get("props", {})) )
|
||||
|
||||
async def load(self):
|
||||
|
@ -56,6 +55,7 @@ class BasicController:
|
|||
|
||||
async def push_udpate(self):
|
||||
self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||
self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||
|
||||
def find_by_id(self, id):
|
||||
return next((item for item in self.data if item.id == id), None)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from cbpi.api.dataclasses import NotificationType
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
|
@ -29,9 +30,10 @@ class DashboardController:
|
|||
return {}
|
||||
|
||||
async def add_content(self, dashboard_id, data):
|
||||
print(data)
|
||||
with open(self.path, 'w') as outfile:
|
||||
json.dump(data, outfile, indent=4, sort_keys=True)
|
||||
self.cbpi.notify(title="Dashboard", message="Saved Successfully", type="success")
|
||||
self.cbpi.notify(title="Dashboard", message="Saved Successfully", type=NotificationType.SUCCESS)
|
||||
return {"status": "OK"}
|
||||
|
||||
async def delete_content(self, dashboard_id):
|
||||
|
|
|
@ -1,18 +1,36 @@
|
|||
import asyncio
|
||||
from cbpi.api.dataclasses import NotificationType
|
||||
import logging
|
||||
import shortuuid
|
||||
class NotificationController:
|
||||
|
||||
def __init__(self, cbpi):
|
||||
'''
|
||||
|
||||
:param cbpi: craftbeerpi object
|
||||
'''
|
||||
self.cbpi = cbpi
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.callback_cache = {}
|
||||
self.listener = {}
|
||||
|
||||
def notify(self, title, message: str, type: str = "info", action=[]) -> None:
|
||||
def add_listener(self, method):
|
||||
listener_id = shortuuid.uuid()
|
||||
self.listener[listener_id] = method
|
||||
return listener_id
|
||||
|
||||
def remove_listener(self, listener_id):
|
||||
try:
|
||||
del self.listener[listener_id]
|
||||
except:
|
||||
self.logger.error("Faild to remove listener {}".format(listener_id))
|
||||
|
||||
async def _call_listener(self, title, message, type, action):
|
||||
for id, method in self.listener.items():
|
||||
print(id, method)
|
||||
asyncio.create_task(method(self.cbpi, title, message, type, action ))
|
||||
|
||||
|
||||
def notify(self, title, message: str, type: NotificationType = NotificationType.INFO, action=[]) -> None:
|
||||
'''
|
||||
This is a convinience method to send notification to the client
|
||||
|
||||
|
@ -29,7 +47,11 @@ class NotificationController:
|
|||
|
||||
actions = list(map(lambda item: prepare_action(item), action))
|
||||
self.callback_cache[notifcation_id] = action
|
||||
self.cbpi.ws.send(dict(id=notifcation_id, topic="notifiaction", type=type, title=title, message=message, action=actions))
|
||||
self.cbpi.ws.send(dict(id=notifcation_id, topic="notifiaction", type=type.value, title=title, message=message, action=actions))
|
||||
data = dict(type=type.value, title=title, message=message, action=actions)
|
||||
self.cbpi.push_update(topic="cbpi/notification", data=data)
|
||||
asyncio.create_task(self._call_listener(title, message, type, action))
|
||||
|
||||
|
||||
def notify_callback(self, notification_id, action_id) -> None:
|
||||
try:
|
||||
|
|
|
@ -1,38 +1,82 @@
|
|||
|
||||
|
||||
|
||||
|
||||
import asyncio
|
||||
|
||||
from asyncio_mqtt import Client, MqttError, Will
|
||||
import json
|
||||
from re import M
|
||||
from asyncio_mqtt import Client, MqttError, Will, client
|
||||
from contextlib import AsyncExitStack, asynccontextmanager
|
||||
from cbpi import __version__
|
||||
import logging
|
||||
|
||||
|
||||
class SatelliteController:
|
||||
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.host = cbpi.static_config.get("mqtt_host", "localhost")
|
||||
self.port = cbpi.static_config.get("mqtt_port", 1883)
|
||||
self.username = cbpi.static_config.get("mqtt_username", None)
|
||||
self.password = cbpi.static_config.get("mqtt_password", None)
|
||||
self.client = None
|
||||
self.topic_filters = [
|
||||
("cbpi/actor/+/on", self._actor_on),
|
||||
("cbpi/actor/+/off", self._actor_off)
|
||||
]
|
||||
self.tasks = set()
|
||||
|
||||
async def init(self):
|
||||
asyncio.create_task(self.init_client(self.cbpi))
|
||||
|
||||
async def publish(self, topic, message):
|
||||
print("MQTT ON")
|
||||
await self.client.publish(topic, message, qos=1)
|
||||
async def publish(self, topic, message, retain=False):
|
||||
if self.client is not None and self.client._connected:
|
||||
try:
|
||||
await self.client.publish(topic, message, qos=1, retain=retain)
|
||||
except:
|
||||
self.logger.warning("Faild to push data via mqtt")
|
||||
|
||||
async def handle_message(self, messages):
|
||||
async def _actor_on(self, messages):
|
||||
async for message in messages:
|
||||
print("FILTERED", message.payload.decode())
|
||||
try:
|
||||
topic_key = message.topic.split("/")
|
||||
await self.cbpi.actor.on(topic_key[2])
|
||||
except:
|
||||
self.logger.warning("Faild to process actor on via mqtt")
|
||||
|
||||
async def handle_unfilterd_message(self, messages):
|
||||
async def _actor_off(self, messages):
|
||||
async for message in messages:
|
||||
print("UNFILTERED", message.payload.decode())
|
||||
try:
|
||||
topic_key = message.topic.split("/")
|
||||
await self.cbpi.actor.off(topic_key[2])
|
||||
except:
|
||||
self.logger.warning("Faild to process actor off via mqtt")
|
||||
|
||||
def subcribe(self, topic, method):
|
||||
task = asyncio.create_task(self._subcribe(topic, method))
|
||||
return task
|
||||
|
||||
async def _subcribe(self, topic, method):
|
||||
while True:
|
||||
try:
|
||||
if self.client._connected.done():
|
||||
async with self.client.filtered_messages(topic) as messages:
|
||||
await self.client.subscribe(topic)
|
||||
async for message in messages:
|
||||
await method(message.payload.decode())
|
||||
except asyncio.CancelledError as e:
|
||||
# Cancel
|
||||
self.logger.warning(
|
||||
"Sub CancelledError Exception: {}".format(e))
|
||||
return
|
||||
except MqttError as e:
|
||||
self.logger.error("Sub MQTT Exception: {}".format(e))
|
||||
except Exception as e:
|
||||
self.logger.error("Sub Exception: {}".format(e))
|
||||
|
||||
# wait before try to resubscribe
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def init_client(self, cbpi):
|
||||
async def log_messages(messages, template):
|
||||
|
||||
async for message in messages:
|
||||
print(template.format(message.payload.decode()))
|
||||
|
||||
async def cancel_tasks(tasks):
|
||||
for task in tasks:
|
||||
|
@ -44,31 +88,32 @@ class SatelliteController:
|
|||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
while True:
|
||||
try:
|
||||
async with AsyncExitStack() as stack:
|
||||
self.tasks = set()
|
||||
stack.push_async_callback(cancel_tasks, self.tasks)
|
||||
self.client = Client(self.host, port=self.port, username=self.username, password=self.password, will=Will(topic="cbpi/diconnect", payload="CBPi Server Disconnected"))
|
||||
|
||||
tasks = set()
|
||||
stack.push_async_callback(cancel_tasks, tasks)
|
||||
|
||||
self.client = Client("localhost", will=Will(topic="cbpi/diconnect", payload="CBPi Server Disconnected"))
|
||||
await stack.enter_async_context(self.client)
|
||||
|
||||
topic_filters = (
|
||||
"cbpi/sensor/#",
|
||||
"cbpi/actor/#"
|
||||
)
|
||||
for topic_filter in topic_filters:
|
||||
# Log all messages that matches the filter
|
||||
manager = self.client.filtered_messages(topic_filter)
|
||||
for topic_filter in self.topic_filters:
|
||||
topic = topic_filter[0]
|
||||
method = topic_filter[1]
|
||||
manager = self.client.filtered_messages(topic)
|
||||
messages = await stack.enter_async_context(manager)
|
||||
task = asyncio.create_task(self.handle_message(messages))
|
||||
tasks.add(task)
|
||||
task = asyncio.create_task(method(messages))
|
||||
self.tasks.add(task)
|
||||
|
||||
messages = await stack.enter_async_context(self.client.unfiltered_messages())
|
||||
task = asyncio.create_task(self.handle_unfilterd_message(messages))
|
||||
tasks.add(task)
|
||||
for topic_filter in self.topic_filters:
|
||||
topic = topic_filter[0]
|
||||
await self.client.subscribe(topic)
|
||||
|
||||
await self.client.subscribe("cbpi/#")
|
||||
await asyncio.gather(*tasks)
|
||||
self.logger.info("MQTT Connected to {}:{}".format(self.host, self.port))
|
||||
await asyncio.gather(*self.tasks)
|
||||
|
||||
except MqttError as e:
|
||||
self.logger.error("MQTT Exception: {}".format(e))
|
||||
except Exception as e:
|
||||
self.logger.error("MQTT General Exception: {}".format(e))
|
||||
await asyncio.sleep(5)
|
||||
|
|
|
@ -78,7 +78,6 @@ class StepController:
|
|||
try:
|
||||
type_cfg = self.types.get(item.type)
|
||||
clazz = type_cfg.get("class")
|
||||
print("CLASS", clazz)
|
||||
item.instance = clazz(self.cbpi, item.id, item.name, item.props, self.done)
|
||||
except Exception as e:
|
||||
logging.warning("Failed to create step instance %s - %s " % (id, e))
|
||||
|
@ -258,6 +257,8 @@ class StepController:
|
|||
else:
|
||||
self.cbpi.ws.send(dict(topic="step_update", data=list(map(lambda item: item.to_dict(), self.profile))))
|
||||
|
||||
self.cbpi.push_update(topic="cbpi/stepupdate", data=list(map(lambda item: item.to_dict(), self.profile)))
|
||||
|
||||
async def start_step(self,step):
|
||||
try:
|
||||
logging.info("Try to start step %s" % step)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
|
||||
import asyncio
|
||||
import json
|
||||
from voluptuous.schema_builder import message
|
||||
from cbpi.api.dataclasses import NotificationType
|
||||
from cbpi.controller.notification_controller import NotificationController
|
||||
import logging
|
||||
from os import urandom
|
||||
|
@ -22,6 +26,7 @@ from cbpi.controller.sensor_controller import SensorController
|
|||
from cbpi.controller.step_controller import StepController
|
||||
from cbpi.controller.recipe_controller import RecipeController
|
||||
from cbpi.controller.system_controller import SystemController
|
||||
from cbpi.controller.satellite_controller import SatelliteController
|
||||
|
||||
from cbpi.controller.log_file_controller import LogController
|
||||
|
||||
|
@ -102,7 +107,10 @@ class CraftBeerPi:
|
|||
self.step : StepController = StepController(self)
|
||||
self.recipe : RecipeController = RecipeController(self)
|
||||
self.notification : NotificationController = NotificationController(self)
|
||||
#self.satellite: SatelliteController = SatelliteController(self)
|
||||
self.satellite = None
|
||||
if self.static_config.get("mqtt", False) is True:
|
||||
self.satellite: SatelliteController = SatelliteController(self)
|
||||
|
||||
self.dashboard = DashboardController(self)
|
||||
self.http_step = StepHttpEndpoints(self)
|
||||
self.http_recipe = RecipeHttpEndpoints(self)
|
||||
|
@ -212,9 +220,11 @@ class CraftBeerPi:
|
|||
|
||||
|
||||
|
||||
def notify(self, title: str, message: str, type: str = "info", action=[]) -> None:
|
||||
def notify(self, title: str, message: str, type: NotificationType = NotificationType.INFO, action=[]) -> None:
|
||||
self.notification.notify(title, message, type, action)
|
||||
|
||||
def push_update(self, topic, data, retain=False) -> None:
|
||||
asyncio.create_task(self.satellite.publish(topic=topic, message=json.dumps(data), retain=retain))
|
||||
|
||||
async def call_initializer(self, app):
|
||||
self.initializer = sorted(self.initializer, key=lambda k: k['order'])
|
||||
|
@ -249,6 +259,8 @@ class CraftBeerPi:
|
|||
await self.job.init()
|
||||
|
||||
await self.config.init()
|
||||
if self.satellite is not None:
|
||||
await self.satellite.init()
|
||||
self._setup_http_index()
|
||||
self.plugin.load_plugins()
|
||||
self.plugin.load_plugins_from_evn()
|
||||
|
@ -259,7 +271,8 @@ class CraftBeerPi:
|
|||
await self.kettle.init()
|
||||
await self.call_initializer(self.app)
|
||||
await self.dashboard.init()
|
||||
#await self.satellite.init()
|
||||
|
||||
|
||||
self._swagger_setup()
|
||||
|
||||
return self.app
|
||||
|
|
|
@ -2,7 +2,7 @@ from socket import timeout
|
|||
from typing import KeysView
|
||||
|
||||
from voluptuous.schema_builder import message
|
||||
from cbpi.api.dataclasses import NotificationAction
|
||||
from cbpi.api.dataclasses import NotificationAction, NotificationType
|
||||
import logging
|
||||
from unittest.mock import MagicMock, patch
|
||||
from datetime import datetime
|
||||
|
@ -17,10 +17,9 @@ class DummyActor(CBPiActor):
|
|||
def __init__(self, cbpi, id, props):
|
||||
super().__init__(cbpi, id, props)
|
||||
|
||||
async def yes(self, **kwargs):
|
||||
print("YES!")
|
||||
await self.cbpi.step.next()
|
||||
|
||||
@action("SAY HELLO", {})
|
||||
async def helloWorld(self, **kwargs):
|
||||
self.cbpi.notify("HELLO", "WOOHO", NotificationType.ERROR)
|
||||
|
||||
async def start(self):
|
||||
await super().start()
|
||||
|
|
|
@ -38,7 +38,7 @@ class MashStep(CBPiStep):
|
|||
self.timer = Timer(int(self.props.Timer) *60 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||
|
||||
async def run(self):
|
||||
while self.running == True:
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
sensor_value = self.get_sensor_value(self.props.Sensor)
|
||||
if sensor_value.get("value") >= int(self.props.Temp) and self.timer.is_running is not True:
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import json
|
||||
|
||||
from cbpi.utils.encoder import ComplexEncoder
|
||||
from hbmqtt.mqtt.constants import QOS_0
|
||||
from hbmqtt.client import MQTTClient
|
||||
from hbmqtt.mqtt.constants import QOS_1, QOS_2
|
||||
from asyncio_mqtt import Client, MqttError, Will
|
||||
import asyncio
|
||||
|
||||
class CBPiMqttClient:
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
self.cbpi.bus.register("#", self.listen)
|
||||
self.client = None
|
||||
self._loop = asyncio.get_event_loop()
|
||||
self._loop.create_task(self.init_client(self.cbpi))
|
||||
|
||||
async def init_client(self, cbpi):
|
||||
|
||||
async with Client("localhost", will=Will(topic="cbpi/diconnect", payload="MY CLIENT"))as client:
|
||||
async with client.filtered_messages("cbpi/#") as messages:
|
||||
await client.subscribe("cbpi/#")
|
||||
async for message in messages:
|
||||
await self.cbpi.actor.on("YwGzXvWMpmbLb6XobesL8n")
|
||||
|
||||
|
||||
|
||||
async def listen(self, topic, **kwargs):
|
||||
if self.client is not None:
|
||||
await self.client.publish(topic, str.encode(json.dumps(kwargs, cls=ComplexEncoder)), QOS_0)
|
||||
|
||||
def setup(cbpi):
|
||||
'''
|
||||
This method is called by the server during startup
|
||||
Here you need to register your plugins at the server
|
||||
|
||||
:param cbpi: the cbpi core
|
||||
:return:
|
||||
'''
|
||||
|
||||
client = CBPiMqttClient(cbpi)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
name: MQTT
|
||||
version: 4
|
||||
active: false
|
49
cbpi/extension/mqtt_sensor/__init__.py
Normal file
49
cbpi/extension/mqtt_sensor/__init__.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import asyncio
|
||||
import random
|
||||
import re
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
|
||||
|
||||
@parameters([Property.Text(label="Topic", configurable=True)])
|
||||
class MQTTSensor(CBPiSensor):
|
||||
|
||||
async def on_message(self, message):
|
||||
try:
|
||||
self.value = message
|
||||
self.log_data(self.value)
|
||||
self.push_update(message)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def __init__(self, cbpi, id, props):
|
||||
super(MQTTSensor, self).__init__(cbpi, id, props)
|
||||
self.mqtt_task = self.cbpi.satellite.subcribe(self.props.Topic, self.on_message)
|
||||
|
||||
async def run(self):
|
||||
while self.running == True:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
def get_state(self):
|
||||
return dict(value=self.value)
|
||||
|
||||
async def on_stop(self):
|
||||
if self.mqtt_task.done() is False:
|
||||
self.mqtt_task.cancel()
|
||||
try:
|
||||
await self.mqtt_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
def setup(cbpi):
|
||||
|
||||
'''
|
||||
This method is called by the server during startup
|
||||
Here you need to register your plugins at the server
|
||||
|
||||
:param cbpi: the cbpi core
|
||||
:return:
|
||||
'''
|
||||
if cbpi.static_config.get("mqtt", False) is True:
|
||||
cbpi.plugin.register("MQTTSensor", MQTTSensor)
|
3
cbpi/extension/mqtt_sensor/config.yaml
Normal file
3
cbpi/extension/mqtt_sensor/config.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
name: DummySensor
|
||||
version: 4
|
||||
active: true
|
|
@ -69,6 +69,7 @@ class DashBoardHttpEndpoints:
|
|||
data = await request.json()
|
||||
dashboard_id = int(request.match_info['id'])
|
||||
await self.cbpi.dashboard.add_content(dashboard_id, data)
|
||||
print("##### SAVE")
|
||||
return web.Response(status=204)
|
||||
|
||||
@request_mapping(path="/{id:\d+}/content", method="DELETE", auth_required=False)
|
||||
|
|
11
venv3/bin/autopep8
Executable file
11
venv3/bin/autopep8
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/Users/manuelfritsch/Documents/git/cbpi4/venv3/bin/python3
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from autopep8 import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
11
venv3/bin/pycodestyle
Executable file
11
venv3/bin/pycodestyle
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/Users/manuelfritsch/Documents/git/cbpi4/venv3/bin/python3
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pycodestyle import _main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(_main())
|
|
@ -0,0 +1,48 @@
|
|||
Main contributors
|
||||
-----------------
|
||||
- Hideo Hattori (https://github.com/hhatto)
|
||||
- Steven Myint (https://github.com/myint)
|
||||
- Bill Wendling (https://github.com/gwelymernans)
|
||||
|
||||
Patches
|
||||
-------
|
||||
- Fraser Tweedale (https://github.com/frasertweedale)
|
||||
- clach04 (https://github.com/clach04)
|
||||
- Marc Abramowitz (https://github.com/msabramo)
|
||||
- dellis23 (https://github.com/dellis23)
|
||||
- Sam Vilain (https://github.com/samv)
|
||||
- Florent Xicluna (https://github.com/florentx)
|
||||
- Andras Tim (https://github.com/andras-tim)
|
||||
- tomscytale (https://github.com/tomscytale)
|
||||
- Filip Noetzel (https://github.com/peritus)
|
||||
- Erik Bray (https://github.com/iguananaut)
|
||||
- Christopher Medrela (https://github.com/chrismedrela)
|
||||
- 小明 (https://github.com/dongweiming)
|
||||
- Andy Hayden (https://github.com/hayd)
|
||||
- Fabio Zadrozny (https://github.com/fabioz)
|
||||
- Alex Chernetz (https://github.com/achernet)
|
||||
- Marc Schlaich (https://github.com/schlamar)
|
||||
- E. M. Bray (https://github.com/embray)
|
||||
- Thomas Hisch (https://github.com/thisch)
|
||||
- Florian Best (https://github.com/spaceone)
|
||||
- Ian Clark (https://github.com/evenicoulddoit)
|
||||
- Khairi Hafsham (https://github.com/khairihafsham)
|
||||
- Neil Halelamien (https://github.com/neilsh)
|
||||
- Hashem Nasarat (https://github.com/Hnasar)
|
||||
- Hugo van Kemenade (https://github.com/hugovk)
|
||||
- gmbnomis (https://github.com/gmbnomis)
|
||||
- Samuel Lelièvre (https://github.com/slel)
|
||||
- bigredengineer (https://github.com/bigredengineer)
|
||||
- Kai Chen (https://github.com/kx-chen)
|
||||
- Anthony Sottile (https://github.com/asottile)
|
||||
- 秋葉 (https://github.com/Hanaasagi)
|
||||
- Christian Clauss (https://github.com/cclauss)
|
||||
- tobixx (https://github.com/tobixx)
|
||||
- bigredengineer (https://github.com/bigredengineer)
|
||||
- Bastien Gérard (https://github.com/bagerard)
|
||||
- nicolasbonifas (https://github.com/nicolasbonifas)
|
||||
- Andrii Yurchuk (https://github.com/Ch00k)
|
||||
- José M. Guisado (https://github.com/pvxe)
|
||||
- Dai Truong (https://github.com/NovaDev94)
|
||||
- jnozsc (https://github.com/jnozsc)
|
||||
- Edwin Shepherd (https://github.com/shardros)
|
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (C) 2010-2011 Hideo Hattori
|
||||
Copyright (C) 2011-2013 Hideo Hattori, Steven Myint
|
||||
Copyright (C) 2013-2016 Hideo Hattori, Steven Myint, Bill Wendling
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
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,453 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: autopep8
|
||||
Version: 1.5.5
|
||||
Summary: A tool that automatically formats Python code to conform to the PEP 8 style guide
|
||||
Home-page: https://github.com/hhatto/autopep8
|
||||
Author: Hideo Hattori
|
||||
Author-email: hhatto.jp@gmail.com
|
||||
License: Expat License
|
||||
Keywords: automation,pep8,format,pycodestyle
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Console
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: OS Independent
|
||||
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.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Software Development :: Quality Assurance
|
||||
Requires-Dist: pycodestyle (>=2.6.0)
|
||||
Requires-Dist: toml
|
||||
|
||||
========
|
||||
autopep8
|
||||
========
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/autopep8.svg
|
||||
:target: https://pypi.org/project/autopep8/
|
||||
:alt: PyPI Version
|
||||
|
||||
.. image:: https://github.com/hhatto/autopep8/workflows/Python%20package/badge.svg
|
||||
:target: https://github.com/hhatto/autopep8/actions
|
||||
:alt: Build status
|
||||
|
||||
.. image:: https://codecov.io/gh/hhatto/autopep8/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/hhatto/autopep8
|
||||
:alt: Code Coverage
|
||||
|
||||
autopep8 automatically formats Python code to conform to the `PEP 8`_ style
|
||||
guide. It uses the pycodestyle_ utility to determine what parts of the code
|
||||
needs to be formatted. autopep8 is capable of fixing most of the formatting
|
||||
issues_ that can be reported by pycodestyle.
|
||||
|
||||
.. _PEP 8: https://www.python.org/dev/peps/pep-0008/
|
||||
.. _issues: https://pycodestyle.readthedocs.org/en/latest/intro.html#error-codes
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
From pip::
|
||||
|
||||
$ pip install --upgrade autopep8
|
||||
|
||||
Consider using the ``--user`` option_.
|
||||
|
||||
.. _option: https://pip.pypa.io/en/latest/user_guide/#user-installs
|
||||
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
autopep8 requires pycodestyle_.
|
||||
|
||||
.. _pycodestyle: https://github.com/PyCQA/pycodestyle
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
To modify a file in place (with aggressive level 2)::
|
||||
|
||||
$ autopep8 --in-place --aggressive --aggressive <filename>
|
||||
|
||||
Before running autopep8.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import math, sys;
|
||||
|
||||
def example1():
|
||||
####This is a long comment. This should be wrapped to fit within 72 characters.
|
||||
some_tuple=( 1,2, 3,'a' );
|
||||
some_variable={'long':'Long code lines should be wrapped within 79 characters.',
|
||||
'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'],
|
||||
'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1,
|
||||
20,300,40000,500000000,60000000000000000]}}
|
||||
return (some_tuple, some_variable)
|
||||
def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key(''));
|
||||
class Example3( object ):
|
||||
def __init__ ( self, bar ):
|
||||
#Comments should have a space after the hash.
|
||||
if bar : bar+=1; bar=bar* bar ; return bar
|
||||
else:
|
||||
some_string = """
|
||||
Indentation in multiline strings should not be touched.
|
||||
Only actual code should be reindented.
|
||||
"""
|
||||
return (sys.path, some_string)
|
||||
|
||||
After running autopep8.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import math
|
||||
import sys
|
||||
|
||||
|
||||
def example1():
|
||||
# This is a long comment. This should be wrapped to fit within 72
|
||||
# characters.
|
||||
some_tuple = (1, 2, 3, 'a')
|
||||
some_variable = {
|
||||
'long': 'Long code lines should be wrapped within 79 characters.',
|
||||
'other': [
|
||||
math.pi,
|
||||
100,
|
||||
200,
|
||||
300,
|
||||
9876543210,
|
||||
'This is a long string that goes on'],
|
||||
'more': {
|
||||
'inner': 'This whole logical line should be wrapped.',
|
||||
some_tuple: [
|
||||
1,
|
||||
20,
|
||||
300,
|
||||
40000,
|
||||
500000000,
|
||||
60000000000000000]}}
|
||||
return (some_tuple, some_variable)
|
||||
|
||||
|
||||
def example2(): return ('' in {'f': 2}) in {'has_key() is deprecated': True}
|
||||
|
||||
|
||||
class Example3(object):
|
||||
def __init__(self, bar):
|
||||
# Comments should have a space after the hash.
|
||||
if bar:
|
||||
bar += 1
|
||||
bar = bar * bar
|
||||
return bar
|
||||
else:
|
||||
some_string = """
|
||||
Indentation in multiline strings should not be touched.
|
||||
Only actual code should be reindented.
|
||||
"""
|
||||
return (sys.path, some_string)
|
||||
|
||||
Options::
|
||||
|
||||
usage: autopep8 [-h] [--version] [-v] [-d] [-i] [--global-config filename]
|
||||
[--ignore-local-config] [-r] [-j n] [-p n] [-a]
|
||||
[--experimental] [--exclude globs] [--list-fixes]
|
||||
[--ignore errors] [--select errors] [--max-line-length n]
|
||||
[--line-range line line] [--hang-closing] [--exit-code]
|
||||
[files [files ...]]
|
||||
|
||||
Automatically formats Python code to conform to the PEP 8 style guide.
|
||||
|
||||
positional arguments:
|
||||
files files to format or '-' for standard in
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--version show program's version number and exit
|
||||
-v, --verbose print verbose messages; multiple -v result in more
|
||||
verbose messages
|
||||
-d, --diff print the diff for the fixed source
|
||||
-i, --in-place make changes to files in place
|
||||
--global-config filename
|
||||
path to a global pep8 config file; if this file does
|
||||
not exist then this is ignored (default:
|
||||
~/.config/pep8)
|
||||
--ignore-local-config
|
||||
don't look for and apply local config files; if not
|
||||
passed, defaults are updated with any config files in
|
||||
the project's root directory
|
||||
-r, --recursive run recursively over directories; must be used with
|
||||
--in-place or --diff
|
||||
-j n, --jobs n number of parallel jobs; match CPU count if value is
|
||||
less than 1
|
||||
-p n, --pep8-passes n
|
||||
maximum number of additional pep8 passes (default:
|
||||
infinite)
|
||||
-a, --aggressive enable non-whitespace changes; multiple -a result in
|
||||
more aggressive changes
|
||||
--experimental enable experimental fixes
|
||||
--exclude globs exclude file/directory names that match these comma-
|
||||
separated globs
|
||||
--list-fixes list codes for fixes; used by --ignore and --select
|
||||
--ignore errors do not fix these errors/warnings (default:
|
||||
E226,E24,W50,W690)
|
||||
--select errors fix only these errors/warnings (e.g. E4,W)
|
||||
--max-line-length n set maximum allowed line length (default: 79)
|
||||
--line-range line line, --range line line
|
||||
only fix errors found within this inclusive range of
|
||||
line numbers (e.g. 1 99); line numbers are indexed at
|
||||
1
|
||||
--hang-closing hang-closing option passed to pycodestyle
|
||||
--exit-code change to behavior of exit code. default behavior of
|
||||
return value, 0 is no differences, 1 is error exit.
|
||||
return 2 when add this option. 2 is exists
|
||||
differences.
|
||||
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
autopep8 fixes the following issues_ reported by pycodestyle_::
|
||||
|
||||
E101 - Reindent all lines.
|
||||
E11 - Fix indentation.
|
||||
E121 - Fix indentation to be a multiple of four.
|
||||
E122 - Add absent indentation for hanging indentation.
|
||||
E123 - Align closing bracket to match opening bracket.
|
||||
E124 - Align closing bracket to match visual indentation.
|
||||
E125 - Indent to distinguish line from next logical line.
|
||||
E126 - Fix over-indented hanging indentation.
|
||||
E127 - Fix visual indentation.
|
||||
E128 - Fix visual indentation.
|
||||
E129 - Fix visual indentation.
|
||||
E131 - Fix hanging indent for unaligned continuation line.
|
||||
E133 - Fix missing indentation for closing bracket.
|
||||
E20 - Remove extraneous whitespace.
|
||||
E211 - Remove extraneous whitespace.
|
||||
E22 - Fix extraneous whitespace around keywords.
|
||||
E224 - Remove extraneous whitespace around operator.
|
||||
E225 - Fix missing whitespace around operator.
|
||||
E226 - Fix missing whitespace around arithmetic operator.
|
||||
E227 - Fix missing whitespace around bitwise/shift operator.
|
||||
E228 - Fix missing whitespace around modulo operator.
|
||||
E231 - Add missing whitespace.
|
||||
E241 - Fix extraneous whitespace around keywords.
|
||||
E242 - Remove extraneous whitespace around operator.
|
||||
E251 - Remove whitespace around parameter '=' sign.
|
||||
E252 - Missing whitespace around parameter equals.
|
||||
E26 - Fix spacing after comment hash for inline comments.
|
||||
E265 - Fix spacing after comment hash for block comments.
|
||||
E266 - Fix too many leading '#' for block comments.
|
||||
E27 - Fix extraneous whitespace around keywords.
|
||||
E301 - Add missing blank line.
|
||||
E302 - Add missing 2 blank lines.
|
||||
E303 - Remove extra blank lines.
|
||||
E304 - Remove blank line following function decorator.
|
||||
E305 - Expected 2 blank lines after end of function or class.
|
||||
E306 - Expected 1 blank line before a nested definition.
|
||||
E401 - Put imports on separate lines.
|
||||
E402 - Fix module level import not at top of file
|
||||
E501 - Try to make lines fit within --max-line-length characters.
|
||||
E502 - Remove extraneous escape of newline.
|
||||
E701 - Put colon-separated compound statement on separate lines.
|
||||
E70 - Put semicolon-separated compound statement on separate lines.
|
||||
E711 - Fix comparison with None.
|
||||
E712 - Fix comparison with boolean.
|
||||
E713 - Use 'not in' for test for membership.
|
||||
E714 - Use 'is not' test for object identity.
|
||||
E721 - Use "isinstance()" instead of comparing types directly.
|
||||
E722 - Fix bare except.
|
||||
E731 - Use a def when use do not assign a lambda expression.
|
||||
W291 - Remove trailing whitespace.
|
||||
W292 - Add a single newline at the end of the file.
|
||||
W293 - Remove trailing whitespace on blank line.
|
||||
W391 - Remove trailing blank lines.
|
||||
W503 - Fix line break before binary operator.
|
||||
W504 - Fix line break after binary operator.
|
||||
W601 - Use "in" rather than "has_key()".
|
||||
W602 - Fix deprecated form of raising exception.
|
||||
W603 - Use "!=" instead of "<>"
|
||||
W604 - Use "repr()" instead of backticks.
|
||||
W605 - Fix invalid escape sequence 'x'.
|
||||
W690 - Fix various deprecated code (via lib2to3).
|
||||
|
||||
autopep8 also fixes some issues not found by pycodestyle_.
|
||||
|
||||
- Correct deprecated or non-idiomatic Python code (via ``lib2to3``). Use this
|
||||
for making Python 2.7 code more compatible with Python 3. (This is triggered
|
||||
if ``W690`` is enabled.)
|
||||
- Normalize files with mixed line endings.
|
||||
- Put a blank line between a class docstring and its first method
|
||||
declaration. (Enabled with ``E301``.)
|
||||
- Remove blank lines between a function declaration and its docstring. (Enabled
|
||||
with ``E303``.)
|
||||
|
||||
autopep8 avoids fixing some issues found by pycodestyle_.
|
||||
|
||||
- ``E112``/``E113`` for non comments are reports of bad indentation that break
|
||||
syntax rules. These should not be modified at all.
|
||||
- ``E265``, which refers to spacing after comment hash, is ignored if the
|
||||
comment looks like code. autopep8 avoids modifying these since they are not
|
||||
real comments. If you really want to get rid of the pycodestyle_ warning,
|
||||
consider just removing the commented-out code. (This can be automated via
|
||||
eradicate_.)
|
||||
|
||||
.. _eradicate: https://github.com/myint/eradicate
|
||||
|
||||
|
||||
More advanced usage
|
||||
===================
|
||||
|
||||
By default autopep8 only makes whitespace changes. Thus, by default, it does
|
||||
not fix ``E711`` and ``E712``. (Changing ``x == None`` to ``x is None`` may
|
||||
change the meaning of the program if ``x`` has its ``__eq__`` method
|
||||
overridden.) Nor does it correct deprecated code ``W6``. To enable these
|
||||
more aggressive fixes, use the ``--aggressive`` option::
|
||||
|
||||
$ autopep8 --aggressive <filename>
|
||||
|
||||
Use multiple ``--aggressive`` to increase the aggressiveness level. For
|
||||
example, ``E712`` requires aggressiveness level 2 (since ``x == True`` could be
|
||||
changed to either ``x`` or ``x is True``, but autopep8 chooses the former).
|
||||
|
||||
``--aggressive`` will also shorten lines more aggressively. It will also remove
|
||||
trailing whitespace more aggressively. (Usually, we don't touch trailing
|
||||
whitespace in docstrings and other multiline strings. And to do even more
|
||||
aggressive changes to docstrings, use docformatter_.)
|
||||
|
||||
.. _docformatter: https://github.com/myint/docformatter
|
||||
|
||||
To enable only a subset of the fixes, use the ``--select`` option. For example,
|
||||
to fix various types of indentation issues::
|
||||
|
||||
$ autopep8 --select=E1,W1 <filename>
|
||||
|
||||
Similarly, to just fix deprecated code::
|
||||
|
||||
$ autopep8 --aggressive --select=W6 <filename>
|
||||
|
||||
The above is useful when trying to port a single code base to work with both
|
||||
Python 2 and Python 3 at the same time.
|
||||
|
||||
If the file being fixed is large, you may want to enable verbose progress
|
||||
messages::
|
||||
|
||||
$ autopep8 -v <filename>
|
||||
|
||||
Passing in ``--experimental`` enables the following functionality:
|
||||
|
||||
- Shortens code lines by taking its length into account
|
||||
|
||||
::
|
||||
|
||||
$ autopep8 --experimental <filename>
|
||||
|
||||
Use as a module
|
||||
===============
|
||||
|
||||
The simplest way of using autopep8 as a module is via the ``fix_code()``
|
||||
function:
|
||||
|
||||
>>> import autopep8
|
||||
>>> autopep8.fix_code('x= 123\n')
|
||||
'x = 123\n'
|
||||
|
||||
Or with options:
|
||||
|
||||
>>> import autopep8
|
||||
>>> autopep8.fix_code('x.has_key(y)\n',
|
||||
... options={'aggressive': 1})
|
||||
'y in x\n'
|
||||
>>> autopep8.fix_code('print( 123 )\n',
|
||||
... options={'ignore': ['E']})
|
||||
'print( 123 )\n'
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
By default, if ``$HOME/.config/pycodestyle`` (``~\.pycodestyle`` in Windows
|
||||
environment) exists, it will be used as global configuration file.
|
||||
Alternatively, you can specify the global configuration file with the
|
||||
``--global-config`` option.
|
||||
|
||||
Also, if ``setup.cfg``, ``tox.ini``, ``.pep8`` and ``.flake8`` files exist
|
||||
in the directory where the target file exists, it will be used as the
|
||||
configuration file.
|
||||
|
||||
``pep8``, ``pycodestyle``, and ``flake8`` can be used as a section.
|
||||
|
||||
configuration file example::
|
||||
|
||||
[pycodestyle]
|
||||
max_line_length = 120
|
||||
ignore = E501
|
||||
|
||||
pyproject.toml
|
||||
--------------
|
||||
|
||||
autopep8 can also use ``pyproject.toml``.
|
||||
section must use ``[tool.autopep8]``, and ``pyproject.toml`` takes precedence
|
||||
over any other configuration files.
|
||||
|
||||
configuration file example::
|
||||
|
||||
[tool.autopep8]
|
||||
max_line_length = 120
|
||||
ignore = "E501,W6" # or ["E501", "W6"]
|
||||
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
Test cases are in ``test/test_autopep8.py``. They can be run directly via
|
||||
``python test/test_autopep8.py`` or via tox_. The latter is useful for
|
||||
testing against multiple Python interpreters. (We currently test against
|
||||
CPython versions 2.7, 3.6 3.7 and 3.8. We also test against PyPy.)
|
||||
|
||||
.. _`tox`: https://pypi.org/project/tox/
|
||||
|
||||
Broad spectrum testing is available via ``test/acid.py``. This script runs
|
||||
autopep8 against Python code and checks for correctness and completeness of the
|
||||
code fixes. It can check that the bytecode remains identical.
|
||||
``test/acid_pypi.py`` makes use of ``acid.py`` to test against the latest
|
||||
released packages on PyPI.
|
||||
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
``pkg_resources.DistributionNotFound``
|
||||
--------------------------------------
|
||||
|
||||
If you are using an ancient version of ``setuptools``, you might encounter
|
||||
``pkg_resources.DistributionNotFound`` when trying to run ``autopep8``. Try
|
||||
upgrading ``setuptools`` to workaround this ``setuptools`` problem::
|
||||
|
||||
$ pip install --upgrade setuptools
|
||||
|
||||
Use ``sudo`` if you are installing to the system.
|
||||
|
||||
|
||||
Links
|
||||
=====
|
||||
|
||||
* PyPI_
|
||||
* GitHub_
|
||||
* `Travis CI`_
|
||||
* Coveralls_
|
||||
|
||||
.. _PyPI: https://pypi.org/project/autopep8/
|
||||
.. _GitHub: https://github.com/hhatto/autopep8
|
||||
.. _`Travis CI`: https://travis-ci.org/hhatto/autopep8
|
||||
.. _`Coveralls`: https://coveralls.io/r/hhatto/autopep8
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
autopep8.py,sha256=xHkIfzd5IPgtTeyNq19o2SLHF28bregdzXEcx3G7ucQ,151970
|
||||
autopep8-1.5.5.dist-info/AUTHORS.rst,sha256=tiTPsbzGl9dtXCMEWXbWSV1zan1M-BoWtiixs46GIWk,2003
|
||||
autopep8-1.5.5.dist-info/LICENSE,sha256=jR0COOSFQ0QZFMqwdB1N4-Bwobg2f3h69fIJr7YLCWo,1181
|
||||
autopep8-1.5.5.dist-info/METADATA,sha256=3IbGpS9FlKP4rzVE6EXbN-0O4D2AZoZcgaXEyYpAq3c,16661
|
||||
autopep8-1.5.5.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
|
||||
autopep8-1.5.5.dist-info/entry_points.txt,sha256=iHNa5_cSXw2ablVbRmfiFGMG1CNrpEPRCEjn3nspaJ8,44
|
||||
autopep8-1.5.5.dist-info/top_level.txt,sha256=s2x-di3QBwGxr7kd5xErt2pom8dsFRdINbmwsOEgLfU,9
|
||||
autopep8-1.5.5.dist-info/RECORD,,
|
||||
../../../bin/autopep8,sha256=nvzzNDnv7luxQekE2qaJPtL_f3kgvxUAJNKivQVar48,253
|
||||
autopep8-1.5.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
__pycache__/autopep8.cpython-37.pyc,,
|
|
@ -0,0 +1,6 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.34.2)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[console_scripts]
|
||||
autopep8 = autopep8:main
|
||||
|
|
@ -0,0 +1 @@
|
|||
autopep8
|
4469
venv3/lib/python3.7/site-packages/autopep8.py
Normal file
4469
venv3/lib/python3.7/site-packages/autopep8.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -0,0 +1,25 @@
|
|||
Copyright © 2006-2009 Johann C. Rocholl <johann@rocholl.net>
|
||||
Copyright © 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
|
||||
Copyright © 2014-2020 Ian Lee <IanLee1521@gmail.com>
|
||||
|
||||
Licensed under the terms of the Expat License
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
pycodestyle.py,sha256=xYcAkNSMHMAlz6cmdkfhwK7QC5RsRwK2pfG2_uPI2xM,103376
|
||||
pycodestyle-2.6.0.dist-info/LICENSE,sha256=93IpXoGvNHjTTojlLQdiACMOx91qOeEjvFyzWqZqva4,1254
|
||||
pycodestyle-2.6.0.dist-info/METADATA,sha256=WI4-bMnR66kT7MKGLVFW7xqmuotPGP0uLJProv7nhD4,30287
|
||||
pycodestyle-2.6.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
|
||||
pycodestyle-2.6.0.dist-info/entry_points.txt,sha256=6JU_7SAppC93MBSQi1_QxDwEQUyg6cgK71ab9q_Hxco,51
|
||||
pycodestyle-2.6.0.dist-info/namespace_packages.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
||||
pycodestyle-2.6.0.dist-info/top_level.txt,sha256=rHbIEiXmvsJ016mFcLVcF_d-dKgP3VdfOB6CWbivZug,12
|
||||
pycodestyle-2.6.0.dist-info/RECORD,,
|
||||
../../../bin/pycodestyle,sha256=pIp6u2iVgkZ61DS1jKVvK_T_RtnyEky3j3Hlf_fhhN8,258
|
||||
pycodestyle-2.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
__pycache__/pycodestyle.cpython-37.pyc,,
|
|
@ -0,0 +1,6 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.34.2)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[console_scripts]
|
||||
pycodestyle = pycodestyle:_main
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
pycodestyle
|
2763
venv3/lib/python3.7/site-packages/pycodestyle.py
Normal file
2763
venv3/lib/python3.7/site-packages/pycodestyle.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -0,0 +1,27 @@
|
|||
The MIT License
|
||||
|
||||
Copyright 2013-2019 William Pearson
|
||||
Copyright 2015-2016 Julien Enselme
|
||||
Copyright 2016 Google Inc.
|
||||
Copyright 2017 Samuel Vasko
|
||||
Copyright 2017 Nate Prewitt
|
||||
Copyright 2017 Jack Evans
|
||||
Copyright 2019 Filippo Broggini
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
255
venv3/lib/python3.7/site-packages/toml-0.10.2.dist-info/METADATA
Normal file
255
venv3/lib/python3.7/site-packages/toml-0.10.2.dist-info/METADATA
Normal file
|
@ -0,0 +1,255 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: toml
|
||||
Version: 0.10.2
|
||||
Summary: Python Library for Tom's Obvious, Minimal Language
|
||||
Home-page: https://github.com/uiri/toml
|
||||
Author: William Pearson
|
||||
Author-email: uiri@xqz.ca
|
||||
License: MIT
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
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 :: 3.9
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
|
||||
|
||||
****
|
||||
TOML
|
||||
****
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/toml
|
||||
:target: https://pypi.org/project/toml/
|
||||
|
||||
.. image:: https://travis-ci.org/uiri/toml.svg?branch=master
|
||||
:target: https://travis-ci.org/uiri/toml
|
||||
|
||||
.. image:: https://img.shields.io/pypi/pyversions/toml.svg
|
||||
:target: https://pypi.org/project/toml/
|
||||
|
||||
|
||||
A Python library for parsing and creating `TOML <https://en.wikipedia.org/wiki/TOML>`_.
|
||||
|
||||
The module passes `the TOML test suite <https://github.com/BurntSushi/toml-test>`_.
|
||||
|
||||
See also:
|
||||
|
||||
* `The TOML Standard <https://github.com/toml-lang/toml>`_
|
||||
* `The currently supported TOML specification <https://github.com/toml-lang/toml/blob/v0.5.0/README.md>`_
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To install the latest release on `PyPI <https://pypi.org/project/toml/>`_,
|
||||
simply run:
|
||||
|
||||
::
|
||||
|
||||
pip install toml
|
||||
|
||||
Or to install the latest development version, run:
|
||||
|
||||
::
|
||||
|
||||
git clone https://github.com/uiri/toml.git
|
||||
cd toml
|
||||
python setup.py install
|
||||
|
||||
Quick Tutorial
|
||||
==============
|
||||
|
||||
*toml.loads* takes in a string containing standard TOML-formatted data and
|
||||
returns a dictionary containing the parsed data.
|
||||
|
||||
.. code:: pycon
|
||||
|
||||
>>> import toml
|
||||
>>> toml_string = """
|
||||
... # This is a TOML document.
|
||||
...
|
||||
... title = "TOML Example"
|
||||
...
|
||||
... [owner]
|
||||
... name = "Tom Preston-Werner"
|
||||
... dob = 1979-05-27T07:32:00-08:00 # First class dates
|
||||
...
|
||||
... [database]
|
||||
... server = "192.168.1.1"
|
||||
... ports = [ 8001, 8001, 8002 ]
|
||||
... connection_max = 5000
|
||||
... enabled = true
|
||||
...
|
||||
... [servers]
|
||||
...
|
||||
... # Indentation (tabs and/or spaces) is allowed but not required
|
||||
... [servers.alpha]
|
||||
... ip = "10.0.0.1"
|
||||
... dc = "eqdc10"
|
||||
...
|
||||
... [servers.beta]
|
||||
... ip = "10.0.0.2"
|
||||
... dc = "eqdc10"
|
||||
...
|
||||
... [clients]
|
||||
... data = [ ["gamma", "delta"], [1, 2] ]
|
||||
...
|
||||
... # Line breaks are OK when inside arrays
|
||||
... hosts = [
|
||||
... "alpha",
|
||||
... "omega"
|
||||
... ]
|
||||
... """
|
||||
>>> parsed_toml = toml.loads(toml_string)
|
||||
|
||||
|
||||
*toml.dumps* takes a dictionary and returns a string containing the
|
||||
corresponding TOML-formatted data.
|
||||
|
||||
.. code:: pycon
|
||||
|
||||
>>> new_toml_string = toml.dumps(parsed_toml)
|
||||
>>> print(new_toml_string)
|
||||
title = "TOML Example"
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
dob = 1979-05-27T07:32:00Z
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002,]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
[clients]
|
||||
data = [ [ "gamma", "delta",], [ 1, 2,],]
|
||||
hosts = [ "alpha", "omega",]
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
|
||||
*toml.dump* takes a dictionary and a file descriptor and returns a string containing the
|
||||
corresponding TOML-formatted data.
|
||||
|
||||
.. code:: pycon
|
||||
|
||||
>>> with open('new_toml_file.toml', 'w') as f:
|
||||
... new_toml_string = toml.dump(parsed_toml, f)
|
||||
>>> print(new_toml_string)
|
||||
title = "TOML Example"
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
dob = 1979-05-27T07:32:00Z
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002,]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
[clients]
|
||||
data = [ [ "gamma", "delta",], [ 1, 2,],]
|
||||
hosts = [ "alpha", "omega",]
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
|
||||
For more functions, view the API Reference below.
|
||||
|
||||
Note
|
||||
----
|
||||
|
||||
For Numpy users, by default the data types ``np.floatX`` will not be translated to floats by toml, but will instead be encoded as strings. To get around this, specify the ``TomlNumpyEncoder`` when saving your data.
|
||||
|
||||
.. code:: pycon
|
||||
|
||||
>>> import toml
|
||||
>>> import numpy as np
|
||||
>>> a = np.arange(0, 10, dtype=np.double)
|
||||
>>> output = {'a': a}
|
||||
>>> toml.dumps(output)
|
||||
'a = [ "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0",]\n'
|
||||
>>> toml.dumps(output, encoder=toml.TomlNumpyEncoder())
|
||||
'a = [ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,]\n'
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
``toml.load(f, _dict=dict)``
|
||||
Parse a file or a list of files as TOML and return a dictionary.
|
||||
|
||||
:Args:
|
||||
* ``f``: A path to a file, list of filepaths (to be read into single
|
||||
object) or a file descriptor
|
||||
* ``_dict``: The class of the dictionary object to be returned
|
||||
|
||||
:Returns:
|
||||
A dictionary (or object ``_dict``) containing parsed TOML data
|
||||
|
||||
:Raises:
|
||||
* ``TypeError``: When ``f`` is an invalid type or is a list containing
|
||||
invalid types
|
||||
* ``TomlDecodeError``: When an error occurs while decoding the file(s)
|
||||
|
||||
``toml.loads(s, _dict=dict)``
|
||||
Parse a TOML-formatted string to a dictionary.
|
||||
|
||||
:Args:
|
||||
* ``s``: The TOML-formatted string to be parsed
|
||||
* ``_dict``: Specifies the class of the returned toml dictionary
|
||||
|
||||
:Returns:
|
||||
A dictionary (or object ``_dict``) containing parsed TOML data
|
||||
|
||||
:Raises:
|
||||
* ``TypeError``: When a non-string object is passed
|
||||
* ``TomlDecodeError``: When an error occurs while decoding the
|
||||
TOML-formatted string
|
||||
|
||||
``toml.dump(o, f, encoder=None)``
|
||||
Write a dictionary to a file containing TOML-formatted data
|
||||
|
||||
:Args:
|
||||
* ``o``: An object to be converted into TOML
|
||||
* ``f``: A File descriptor where the TOML-formatted output should be stored
|
||||
* ``encoder``: An instance of ``TomlEncoder`` (or subclass) for encoding the object. If ``None``, will default to ``TomlEncoder``
|
||||
|
||||
:Returns:
|
||||
A string containing the TOML-formatted data corresponding to object ``o``
|
||||
|
||||
:Raises:
|
||||
* ``TypeError``: When anything other than file descriptor is passed
|
||||
|
||||
``toml.dumps(o, encoder=None)``
|
||||
Create a TOML-formatted string from an input object
|
||||
|
||||
:Args:
|
||||
* ``o``: An object to be converted into TOML
|
||||
* ``encoder``: An instance of ``TomlEncoder`` (or subclass) for encoding the object. If ``None``, will default to ``TomlEncoder``
|
||||
|
||||
:Returns:
|
||||
A string containing the TOML-formatted data corresponding to object ``o``
|
||||
|
||||
|
||||
|
||||
Licensing
|
||||
=========
|
||||
|
||||
This project is released under the terms of the MIT Open Source License. View
|
||||
*LICENSE.txt* for more information.
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
toml/__init__.py,sha256=Au3kqCwKD0cjbf4yJGOpUFwpsY0WHsC1ZRGvWgIKmpc,723
|
||||
toml/decoder.py,sha256=hSGTLf-2WBDZ_ddoCHWFy6N647XyMSh1o3rN2o4dEFg,38942
|
||||
toml/encoder.py,sha256=XjBc8ayvvlsLyd_qDA4tMWDNmMFRS4DpwtuDSWBq7zo,9940
|
||||
toml/ordered.py,sha256=mz03lZmV0bmc9lsYRIUOuj7Dsu5Ptwq-UtGVq5FdVZ4,354
|
||||
toml/tz.py,sha256=-5vg8wkg_atnVi2TnEveexIVE7T_FxBVr_-2WVfO1oA,701
|
||||
toml-0.10.2.dist-info/LICENSE,sha256=LZKUgj32yJNXyL5JJ_znk2HWVh5e51MtWSbmOTmqpTY,1252
|
||||
toml-0.10.2.dist-info/METADATA,sha256=n_YkspvEihd_QXLIZZ50WVSFz3rZ_k7jQP-OU1WUpWY,7142
|
||||
toml-0.10.2.dist-info/WHEEL,sha256=ADKeyaGyKF5DwBNE0sRE5pvW-bSkFMJfBuhzZ3rceP4,110
|
||||
toml-0.10.2.dist-info/top_level.txt,sha256=2BO8ZRNnvJWgXyiQv66LBb_v87qBzcoUtEBefA75Ouk,5
|
||||
toml-0.10.2.dist-info/RECORD,,
|
||||
toml-0.10.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
toml/__pycache__/ordered.cpython-37.pyc,,
|
||||
toml/__pycache__/tz.cpython-37.pyc,,
|
||||
toml/__pycache__/decoder.cpython-37.pyc,,
|
||||
toml/__pycache__/encoder.cpython-37.pyc,,
|
||||
toml/__pycache__/__init__.cpython-37.pyc,,
|
|
@ -0,0 +1,6 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.35.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
|
@ -0,0 +1 @@
|
|||
toml
|
25
venv3/lib/python3.7/site-packages/toml/__init__.py
Normal file
25
venv3/lib/python3.7/site-packages/toml/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
"""Python module which parses and emits TOML.
|
||||
|
||||
Released under the MIT license.
|
||||
"""
|
||||
|
||||
from toml import encoder
|
||||
from toml import decoder
|
||||
|
||||
__version__ = "0.10.2"
|
||||
_spec_ = "0.5.0"
|
||||
|
||||
load = decoder.load
|
||||
loads = decoder.loads
|
||||
TomlDecoder = decoder.TomlDecoder
|
||||
TomlDecodeError = decoder.TomlDecodeError
|
||||
TomlPreserveCommentDecoder = decoder.TomlPreserveCommentDecoder
|
||||
|
||||
dump = encoder.dump
|
||||
dumps = encoder.dumps
|
||||
TomlEncoder = encoder.TomlEncoder
|
||||
TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder
|
||||
TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder
|
||||
TomlNumpyEncoder = encoder.TomlNumpyEncoder
|
||||
TomlPreserveCommentEncoder = encoder.TomlPreserveCommentEncoder
|
||||
TomlPathlibEncoder = encoder.TomlPathlibEncoder
|
1057
venv3/lib/python3.7/site-packages/toml/decoder.py
Normal file
1057
venv3/lib/python3.7/site-packages/toml/decoder.py
Normal file
File diff suppressed because it is too large
Load diff
304
venv3/lib/python3.7/site-packages/toml/encoder.py
Normal file
304
venv3/lib/python3.7/site-packages/toml/encoder.py
Normal file
|
@ -0,0 +1,304 @@
|
|||
import datetime
|
||||
import re
|
||||
import sys
|
||||
from decimal import Decimal
|
||||
|
||||
from toml.decoder import InlineTableDict
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
unicode = str
|
||||
|
||||
|
||||
def dump(o, f, encoder=None):
|
||||
"""Writes out dict as toml to a file
|
||||
|
||||
Args:
|
||||
o: Object to dump into toml
|
||||
f: File descriptor where the toml should be stored
|
||||
encoder: The ``TomlEncoder`` to use for constructing the output string
|
||||
|
||||
Returns:
|
||||
String containing the toml corresponding to dictionary
|
||||
|
||||
Raises:
|
||||
TypeError: When anything other than file descriptor is passed
|
||||
"""
|
||||
|
||||
if not f.write:
|
||||
raise TypeError("You can only dump an object to a file descriptor")
|
||||
d = dumps(o, encoder=encoder)
|
||||
f.write(d)
|
||||
return d
|
||||
|
||||
|
||||
def dumps(o, encoder=None):
|
||||
"""Stringifies input dict as toml
|
||||
|
||||
Args:
|
||||
o: Object to dump into toml
|
||||
encoder: The ``TomlEncoder`` to use for constructing the output string
|
||||
|
||||
Returns:
|
||||
String containing the toml corresponding to dict
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import toml
|
||||
>>> output = {
|
||||
... 'a': "I'm a string",
|
||||
... 'b': ["I'm", "a", "list"],
|
||||
... 'c': 2400
|
||||
... }
|
||||
>>> toml.dumps(output)
|
||||
'a = "I\'m a string"\nb = [ "I\'m", "a", "list",]\nc = 2400\n'
|
||||
```
|
||||
"""
|
||||
|
||||
retval = ""
|
||||
if encoder is None:
|
||||
encoder = TomlEncoder(o.__class__)
|
||||
addtoretval, sections = encoder.dump_sections(o, "")
|
||||
retval += addtoretval
|
||||
outer_objs = [id(o)]
|
||||
while sections:
|
||||
section_ids = [id(section) for section in sections.values()]
|
||||
for outer_obj in outer_objs:
|
||||
if outer_obj in section_ids:
|
||||
raise ValueError("Circular reference detected")
|
||||
outer_objs += section_ids
|
||||
newsections = encoder.get_empty_table()
|
||||
for section in sections:
|
||||
addtoretval, addtosections = encoder.dump_sections(
|
||||
sections[section], section)
|
||||
|
||||
if addtoretval or (not addtoretval and not addtosections):
|
||||
if retval and retval[-2:] != "\n\n":
|
||||
retval += "\n"
|
||||
retval += "[" + section + "]\n"
|
||||
if addtoretval:
|
||||
retval += addtoretval
|
||||
for s in addtosections:
|
||||
newsections[section + "." + s] = addtosections[s]
|
||||
sections = newsections
|
||||
return retval
|
||||
|
||||
|
||||
def _dump_str(v):
|
||||
if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str):
|
||||
v = v.decode('utf-8')
|
||||
v = "%r" % v
|
||||
if v[0] == 'u':
|
||||
v = v[1:]
|
||||
singlequote = v.startswith("'")
|
||||
if singlequote or v.startswith('"'):
|
||||
v = v[1:-1]
|
||||
if singlequote:
|
||||
v = v.replace("\\'", "'")
|
||||
v = v.replace('"', '\\"')
|
||||
v = v.split("\\x")
|
||||
while len(v) > 1:
|
||||
i = -1
|
||||
if not v[0]:
|
||||
v = v[1:]
|
||||
v[0] = v[0].replace("\\\\", "\\")
|
||||
# No, I don't know why != works and == breaks
|
||||
joinx = v[0][i] != "\\"
|
||||
while v[0][:i] and v[0][i] == "\\":
|
||||
joinx = not joinx
|
||||
i -= 1
|
||||
if joinx:
|
||||
joiner = "x"
|
||||
else:
|
||||
joiner = "u00"
|
||||
v = [v[0] + joiner + v[1]] + v[2:]
|
||||
return unicode('"' + v[0] + '"')
|
||||
|
||||
|
||||
def _dump_float(v):
|
||||
return "{}".format(v).replace("e+0", "e+").replace("e-0", "e-")
|
||||
|
||||
|
||||
def _dump_time(v):
|
||||
utcoffset = v.utcoffset()
|
||||
if utcoffset is None:
|
||||
return v.isoformat()
|
||||
# The TOML norm specifies that it's local time thus we drop the offset
|
||||
return v.isoformat()[:-6]
|
||||
|
||||
|
||||
class TomlEncoder(object):
|
||||
|
||||
def __init__(self, _dict=dict, preserve=False):
|
||||
self._dict = _dict
|
||||
self.preserve = preserve
|
||||
self.dump_funcs = {
|
||||
str: _dump_str,
|
||||
unicode: _dump_str,
|
||||
list: self.dump_list,
|
||||
bool: lambda v: unicode(v).lower(),
|
||||
int: lambda v: v,
|
||||
float: _dump_float,
|
||||
Decimal: _dump_float,
|
||||
datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'),
|
||||
datetime.time: _dump_time,
|
||||
datetime.date: lambda v: v.isoformat()
|
||||
}
|
||||
|
||||
def get_empty_table(self):
|
||||
return self._dict()
|
||||
|
||||
def dump_list(self, v):
|
||||
retval = "["
|
||||
for u in v:
|
||||
retval += " " + unicode(self.dump_value(u)) + ","
|
||||
retval += "]"
|
||||
return retval
|
||||
|
||||
def dump_inline_table(self, section):
|
||||
"""Preserve inline table in its compact syntax instead of expanding
|
||||
into subsection.
|
||||
|
||||
https://github.com/toml-lang/toml#user-content-inline-table
|
||||
"""
|
||||
retval = ""
|
||||
if isinstance(section, dict):
|
||||
val_list = []
|
||||
for k, v in section.items():
|
||||
val = self.dump_inline_table(v)
|
||||
val_list.append(k + " = " + val)
|
||||
retval += "{ " + ", ".join(val_list) + " }\n"
|
||||
return retval
|
||||
else:
|
||||
return unicode(self.dump_value(section))
|
||||
|
||||
def dump_value(self, v):
|
||||
# Lookup function corresponding to v's type
|
||||
dump_fn = self.dump_funcs.get(type(v))
|
||||
if dump_fn is None and hasattr(v, '__iter__'):
|
||||
dump_fn = self.dump_funcs[list]
|
||||
# Evaluate function (if it exists) else return v
|
||||
return dump_fn(v) if dump_fn is not None else self.dump_funcs[str](v)
|
||||
|
||||
def dump_sections(self, o, sup):
|
||||
retstr = ""
|
||||
if sup != "" and sup[-1] != ".":
|
||||
sup += '.'
|
||||
retdict = self._dict()
|
||||
arraystr = ""
|
||||
for section in o:
|
||||
section = unicode(section)
|
||||
qsection = section
|
||||
if not re.match(r'^[A-Za-z0-9_-]+$', section):
|
||||
qsection = _dump_str(section)
|
||||
if not isinstance(o[section], dict):
|
||||
arrayoftables = False
|
||||
if isinstance(o[section], list):
|
||||
for a in o[section]:
|
||||
if isinstance(a, dict):
|
||||
arrayoftables = True
|
||||
if arrayoftables:
|
||||
for a in o[section]:
|
||||
arraytabstr = "\n"
|
||||
arraystr += "[[" + sup + qsection + "]]\n"
|
||||
s, d = self.dump_sections(a, sup + qsection)
|
||||
if s:
|
||||
if s[0] == "[":
|
||||
arraytabstr += s
|
||||
else:
|
||||
arraystr += s
|
||||
while d:
|
||||
newd = self._dict()
|
||||
for dsec in d:
|
||||
s1, d1 = self.dump_sections(d[dsec], sup +
|
||||
qsection + "." +
|
||||
dsec)
|
||||
if s1:
|
||||
arraytabstr += ("[" + sup + qsection +
|
||||
"." + dsec + "]\n")
|
||||
arraytabstr += s1
|
||||
for s1 in d1:
|
||||
newd[dsec + "." + s1] = d1[s1]
|
||||
d = newd
|
||||
arraystr += arraytabstr
|
||||
else:
|
||||
if o[section] is not None:
|
||||
retstr += (qsection + " = " +
|
||||
unicode(self.dump_value(o[section])) + '\n')
|
||||
elif self.preserve and isinstance(o[section], InlineTableDict):
|
||||
retstr += (qsection + " = " +
|
||||
self.dump_inline_table(o[section]))
|
||||
else:
|
||||
retdict[qsection] = o[section]
|
||||
retstr += arraystr
|
||||
return (retstr, retdict)
|
||||
|
||||
|
||||
class TomlPreserveInlineDictEncoder(TomlEncoder):
|
||||
|
||||
def __init__(self, _dict=dict):
|
||||
super(TomlPreserveInlineDictEncoder, self).__init__(_dict, True)
|
||||
|
||||
|
||||
class TomlArraySeparatorEncoder(TomlEncoder):
|
||||
|
||||
def __init__(self, _dict=dict, preserve=False, separator=","):
|
||||
super(TomlArraySeparatorEncoder, self).__init__(_dict, preserve)
|
||||
if separator.strip() == "":
|
||||
separator = "," + separator
|
||||
elif separator.strip(' \t\n\r,'):
|
||||
raise ValueError("Invalid separator for arrays")
|
||||
self.separator = separator
|
||||
|
||||
def dump_list(self, v):
|
||||
t = []
|
||||
retval = "["
|
||||
for u in v:
|
||||
t.append(self.dump_value(u))
|
||||
while t != []:
|
||||
s = []
|
||||
for u in t:
|
||||
if isinstance(u, list):
|
||||
for r in u:
|
||||
s.append(r)
|
||||
else:
|
||||
retval += " " + unicode(u) + self.separator
|
||||
t = s
|
||||
retval += "]"
|
||||
return retval
|
||||
|
||||
|
||||
class TomlNumpyEncoder(TomlEncoder):
|
||||
|
||||
def __init__(self, _dict=dict, preserve=False):
|
||||
import numpy as np
|
||||
super(TomlNumpyEncoder, self).__init__(_dict, preserve)
|
||||
self.dump_funcs[np.float16] = _dump_float
|
||||
self.dump_funcs[np.float32] = _dump_float
|
||||
self.dump_funcs[np.float64] = _dump_float
|
||||
self.dump_funcs[np.int16] = self._dump_int
|
||||
self.dump_funcs[np.int32] = self._dump_int
|
||||
self.dump_funcs[np.int64] = self._dump_int
|
||||
|
||||
def _dump_int(self, v):
|
||||
return "{}".format(int(v))
|
||||
|
||||
|
||||
class TomlPreserveCommentEncoder(TomlEncoder):
|
||||
|
||||
def __init__(self, _dict=dict, preserve=False):
|
||||
from toml.decoder import CommentValue
|
||||
super(TomlPreserveCommentEncoder, self).__init__(_dict, preserve)
|
||||
self.dump_funcs[CommentValue] = lambda v: v.dump(self.dump_value)
|
||||
|
||||
|
||||
class TomlPathlibEncoder(TomlEncoder):
|
||||
|
||||
def _dump_pathlib_path(self, v):
|
||||
return _dump_str(str(v))
|
||||
|
||||
def dump_value(self, v):
|
||||
if (3, 4) <= sys.version_info:
|
||||
import pathlib
|
||||
if isinstance(v, pathlib.PurePath):
|
||||
v = str(v)
|
||||
return super(TomlPathlibEncoder, self).dump_value(v)
|
15
venv3/lib/python3.7/site-packages/toml/ordered.py
Normal file
15
venv3/lib/python3.7/site-packages/toml/ordered.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from collections import OrderedDict
|
||||
from toml import TomlEncoder
|
||||
from toml import TomlDecoder
|
||||
|
||||
|
||||
class TomlOrderedDecoder(TomlDecoder):
|
||||
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__(_dict=OrderedDict)
|
||||
|
||||
|
||||
class TomlOrderedEncoder(TomlEncoder):
|
||||
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__(_dict=OrderedDict)
|
24
venv3/lib/python3.7/site-packages/toml/tz.py
Normal file
24
venv3/lib/python3.7/site-packages/toml/tz.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
from datetime import tzinfo, timedelta
|
||||
|
||||
|
||||
class TomlTz(tzinfo):
|
||||
def __init__(self, toml_offset):
|
||||
if toml_offset == "Z":
|
||||
self._raw_offset = "+00:00"
|
||||
else:
|
||||
self._raw_offset = toml_offset
|
||||
self._sign = -1 if self._raw_offset[0] == '-' else 1
|
||||
self._hours = int(self._raw_offset[1:3])
|
||||
self._minutes = int(self._raw_offset[4:6])
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return self.__class__(self._raw_offset)
|
||||
|
||||
def tzname(self, dt):
|
||||
return "UTC" + self._raw_offset
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self._sign * timedelta(hours=self._hours, minutes=self._minutes)
|
||||
|
||||
def dst(self, dt):
|
||||
return timedelta(0)
|
|
@ -1 +1 @@
|
|||
{"last_check":"2021-03-03T22:25:46Z","pypi_version":"21.0.1"}
|
||||
{"last_check":"2021-03-14T08:43:16Z","pypi_version":"21.0.1"}
|
Loading…
Reference in a new issue