Skip to content

Commit 8ec3b00

Browse files
committed
version 1.0.3
see changelog
1 parent 351ca51 commit 8ec3b00

File tree

105 files changed

+1675
-309
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+1675
-309
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,11 @@ update(verbose=True, path="some/path") # equivalent to 'draftsman-update -v -p s
139139
Both `mod-info.json` and `mod-settings.dat` are recognized by `draftsman-update`, so you can also just change the settings in either of those and the loading process will adjust as well.
140140

141141
## TODO
142-
* Investigate `deal` and improve user experience with errors and warnings
143142
* Add warnings for placement constraints on rails, rail signals and train stops
144143
* Add constraints on `UpgradePlanner` and `DeconstructionPlanner`
145144
* `Blueprint.schedules` convenience functions
146145
* More doctests
146+
* Write test cases for `dump_format`
147147
* Add plaintext representations of Entity JSON objects for all entities in addition to blueprintables
148148
* Update modding documentation guide to reflect 2.0 changes
149149
* Reevaluate the diamond diagrams for inherited `Entity` subclass
@@ -153,4 +153,6 @@ Both `mod-info.json` and `mod-settings.dat` are recognized by `draftsman-update`
153153
* RailPlanner (specify rail paths via turtle-like commands)
154154
* Custom `data.raw` extraction and formatting?
155155
* Maybe integrate defaults for more succinct blueprint strings?
156+
* Unify entity validation into one monolithic thing
157+
* Investigate more performant alternatives to `schema` (validir? requires cython, currently we're pure python)
156158
* Look into Lua (or other language) bindings via backport to C/Cython

changelog.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## 1.0.3
4+
* Updated `factorio-data` to version `1.1.76` (latest stable)
5+
* Updated `compatibility/defines.lua` to `1.1.76` (latest stable)
6+
* Merged penguincounter's pull request:
7+
* Fixed logistics requester and buffer chest signatures not having the correct `circuit_mode_of_operation` key
8+
* Added a `dump_format()` method to entities that outputs a user friendly description of all of the possible key/value entries in the exported blueprint dict
9+
* Still needs to be done for Blueprintable; ideally there would be a `Exportable` parent class that would implement the madness
10+
* Also need to investigate faster validation options since schema is pretty slow; maybe we can unify and improve speed at the same time
11+
* Added a `get_format()` method intended to get a readable formatted string (that can easily be autogenerated at the top of each entity in the documentation!)
12+
* Changed `_exports` dict to be both more user readable and only defined on a per class basis instead of a per instance basis (so memory usage should be down)
13+
* Prepped `env.py` for when Lupa version 2.0 goes live (which will resolve #50)
14+
* Fixed `"Mining_Drones_Harder"` mod not loading because of stray "__MACOSX" folder defined alongside (#55)
15+
* Fixed `"FactorioExtended-Plus-Logistics"` not loading due to internal file titled `__init__.lua` (#56)
16+
* Fixed `env.extract_entities().categorize_entities()` to `get` flags instead of assuming they exist (`"flags"` set is common but optional)
17+
318
## 1.0.2
419
* Added `UpgradePlanner` and `DeconstructionPlanner` (#40)
520
* Created an abstract class `Blueprintable` which now implements `Blueprint`, `BlueprintBook`, `UpgradePlanner`, and `DeconstructionPlanner` to increase code reuse
@@ -44,7 +59,6 @@
4459
* Split the `signatures.CONTROL_BEHAVIOR` into many sub implementations, which should improve both safety and (hopefully) performance
4560
* Fixed #24, #25, #27, #32, #33, and #34
4661

47-
4862
## 0.9.7
4963
* Merged louga31's pull request
5064
* Rewrite of the `_shift_key_indices` in `EntityList` to make it faster using list comprehension

draftsman/_factorio_version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# _factorio_version.py
22

3-
__factorio_version__ = "1.1.61.0"
4-
__factorio_version_info__ = (1, 1, 61, 0)
3+
__factorio_version__ = "1.1.76.0"
4+
__factorio_version_info__ = (1, 1, 76, 0)

draftsman/_version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# version.py
22

3-
__version__ = "1.0.2"
4-
__version_info__ = (1, 0, 2)
3+
__version__ = "1.0.3"
4+
__version_info__ = (1, 0, 3)

draftsman/classes/blueprintbook.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{
88
"blueprint_book": {
99
"item": "blueprint-book", # The associated item with this structure
10-
"label": str, # A user given name for this deconstruction planner
10+
"label": str, # A user given name for this blueprint book planner
1111
"label_color": { # The overall color of the label
1212
"r": float[0.0, 1.0] or int[0, 255], # red
1313
"g": float[0.0, 1.0] or int[0, 255], # green

draftsman/classes/entity.py

Lines changed: 121 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
from draftsman import utils
1818

1919
import copy
20+
import json
2021
from typing import Union, Callable
22+
from schema import Schema
2123
import six
2224

2325

@@ -28,6 +30,67 @@ class Entity(EntityLike):
2830
implemented in :py:mod:`draftsman.prototypes`.
2931
"""
3032

33+
# A dictionary containing all of the valid keys used in exported blueprint
34+
# strings.
35+
# Updated on a per Entity and Mixin basis; so ``Entity._exports`` will
36+
# differ from ``ConstantCombinator._exports``.
37+
# TODO: this needs to be some kind of recursive structure so that the data
38+
# for things like "control_behavior" aren't displayed flat
39+
_exports = {
40+
"name": {
41+
"format": "str",
42+
"description": "Name of the entity",
43+
"required": True,
44+
},
45+
"position": {
46+
"format": "{'x': float, 'y': float}",
47+
"description": "Position of the entity in the blueprint/world",
48+
"required": True,
49+
"transform": (lambda self, _: getattr(self, "global_position").to_dict()),
50+
},
51+
"tags": {
52+
"format": "{...}",
53+
"description": (
54+
"Any custom data associated with the entity; used for modded data "
55+
"primarily"
56+
),
57+
"required": lambda x: x, # Only optional if empty
58+
},
59+
}
60+
61+
@classmethod
62+
def dump_format(cls):
63+
# type: () -> dict
64+
"""
65+
Dumps a JSON-serializable representation of the Entity's ``exports``
66+
dictionary, primarily for user interpretation.
67+
68+
:returns: A JSON dictionary containing the names of each valid key, a
69+
short description of their purpose, and whether or not they're
70+
considered optional.
71+
"""
72+
return {
73+
name: {
74+
"format": export["format"],
75+
"description": export["description"],
76+
"optional": False if export["required"] is True else True,
77+
}
78+
for name, export in cls._exports.items()
79+
} # pragma: no coverage
80+
81+
@classmethod
82+
def get_format(cls):
83+
# type: () -> str
84+
"""
85+
Produces a pretty string representation of ``meth:dump_format``. Work in
86+
progress.
87+
88+
:returns: A formatted string that can be output to stdout or file.
89+
"""
90+
return json.dumps(cls.dump_format(), indent=4) # pragma: no coverage
91+
92+
# =========================================================================
93+
3194
def __init__(self, name, similar_entities, tile_position=[0, 0], **kwargs):
3295
# type: (str, list[str], Union[list, dict], **dict) -> None
3396
"""
@@ -50,9 +113,7 @@ def __init__(self, name, similar_entities, tile_position=[0, 0], **kwargs):
50113
"""
51114
# Init EntityLike
52115
super(Entity, self).__init__()
53-
# Create a set of keywords that transfer in to_dict function
54-
# Since some things we want to keep internal without sending to to_dict
55-
self.exports = dict()
116+
56117
# For user convinience, keep track of all the unused arguments, and
57118
# issue a warning if the user provided one that was not used.
58119
self.unused_args = kwargs
@@ -68,7 +129,6 @@ def __init__(self, name, similar_entities, tile_position=[0, 0], **kwargs):
68129
)
69130
)
70131
self._name = six.text_type(name)
71-
self._add_export("name")
72132

73133
# Entity type
74134
self._type = entities.raw[self.name]["type"]
@@ -131,16 +191,12 @@ def __init__(self, name, similar_entities, tile_position=[0, 0], **kwargs):
131191
self.unused_args.pop("position")
132192
else:
133193
self.tile_position = tile_position
134-
self._add_export(
135-
"global_position", None, lambda k, v: ("position", v.to_dict())
136-
)
137194

138195
# Entity tags
139196
self.tags = {}
140197
if "tags" in kwargs:
141198
self.tags = kwargs["tags"]
142199
self.unused_args.pop("tags")
143-
self._add_export("tags", lambda x: x)
144200

145201
# Remove entity_number if we're importing from a dict
146202
self.unused_args.pop("entity_number", None)
@@ -513,20 +569,34 @@ def to_dict(self):
513569
514570
:returns: The exported JSON-dict representation of the Entity.
515571
"""
516-
# Only add the keys in the exports dictionary
572+
# out = {}
573+
# for name, funcs in self.exports.items():
574+
# value = getattr(self, name)
575+
# criterion = funcs[0]
576+
# formatter = funcs[1]
577+
# # Does the value match the criteria to be included?
578+
# if criterion is None or criterion(value):
579+
# if formatter is not None:
580+
# # Normalize key/value pair
581+
# k, v = formatter(name, value)
582+
# else:
583+
# k, v = name, value
584+
# out[k] = v
585+
586+
# return out
587+
517588
out = {}
518-
for name, funcs in self.exports.items():
519-
value = getattr(self, name)
520-
criterion = funcs[0]
521-
formatter = funcs[1]
522-
# Does the value match the criteria to be included?
523-
if criterion is None or criterion(value):
524-
if formatter is not None:
525-
# Normalize key/value pair
526-
k, v = formatter(name, value)
527-
else:
528-
k, v = name, value
529-
out[k] = v
589+
for name, export in self.__class__._exports.items():
590+
transform = export.get("transform", None)
591+
if transform is not None:
592+
value = transform(self, name)
593+
else:
594+
value = getattr(self, name)
595+
596+
required = export.get("required", None)
597+
# print(required)
598+
if required is True or required and required(value):
599+
out[name] = value
530600

531601
return out
532602

@@ -662,36 +732,36 @@ def merge_circuit_connection(self, side, color, point, other):
662732
# (make sure to make a copy in case the original data gets deleted)
663733
self.tags = copy.deepcopy(other.tags)
664734

665-
def _add_export(self, name, criterion=None, formatter=None):
666-
# type: (str, Callable, Callable) -> None
667-
"""
668-
Adds a key to ``exports`` with an optional criteria and formatting
669-
function.
670-
671-
We can't just convert the entire entity to a dict, because there are a
672-
number of keys (for technical or space reasons) that we dont want to
673-
add to the dictionary. Instead, we keep track of the keys we do want
674-
(``exports``) and add those if they're present in the Entity object.
675-
676-
However, some items that are present in Entity might be initialized to
677-
``None`` or otherwise redundant values, which would just take up space
678-
in the output dict. Hence, we can also provide a criteria function that
679-
takes a single argument, the value of the element in the `Entity`. If
680-
the function returns ``True``, the key is added to the output dictionary.
681-
If the function is ``None``, the key is always added.
682-
683-
This function also supports an optional ``formatter`` function that
684-
takes two arguments, the ``key`` and ``value`` pair and returns a tuple
685-
of the two in the same order. This allows to perform any modification to
686-
the key or value before being added to the output dict.
687-
688-
:param name: The name of the attribute that you would like to keep.
689-
:param criterion: Function that determines whether or not the attribute
690-
should be added.
691-
:param formatter: Function that determines the output format of the
692-
key-value pair in the output dictionary.
693-
"""
694-
self.exports[name] = [criterion, formatter]
735+
# def _add_export(self, name, criterion=None, formatter=None):
736+
# # type: (str, Callable, Callable) -> None
737+
# """
738+
# Adds a key to ``exports`` with an optional criteria and formatting
739+
# function.
740+
741+
# We can't just convert the entire entity to a dict, because there are a
742+
# number of keys (for technical or space reasons) that we dont want to
743+
# add to the dictionary. Instead, we keep track of the keys we do want
744+
# (``exports``) and add those if they're present in the Entity object.
745+
746+
# However, some items that are present in Entity might be initialized to
747+
# ``None`` or otherwise redundant values, which would just take up space
748+
# in the output dict. Hence, we can also provide a criteria function that
749+
# takes a single argument, the value of the element in the `Entity`. If
750+
# the function returns ``True``, the key is added to the output dictionary.
751+
# If the function is ``None``, the key is always added.
752+
753+
# This function also supports an optional ``formatter`` function that
754+
# takes two arguments, the ``key`` and ``value`` pair and returns a tuple
755+
# of the two in the same order. This allows to perform any modification to
756+
# the key or value before being added to the output dict.
757+
758+
# :param name: The name of the attribute that you would like to keep.
759+
# :param criterion: Function that determines whether or not the attribute
760+
# should be added.
761+
# :param formatter: Function that determines the output format of the
762+
# key-value pair in the output dictionary.
763+
# """
764+
# self.exports[name] = [criterion, formatter]
695765

696766
def __repr__(self): # pragma: no coverage
697767
# type: () -> str

draftsman/classes/mixins/circuit_condition.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class CircuitConditionMixin(object): # (ControlBehaviorMixin)
1414
value of some signal exceeds some constant.
1515
"""
1616

17+
_exports = {}
18+
1719
def set_circuit_condition(self, a=None, cmp="<", b=0):
1820
# type: (str, str, Union[str, int]) -> None
1921
"""

draftsman/classes/mixins/circuit_connectable.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ class CircuitConnectableMixin(object):
1616
Enables the Entity to be connected to circuit networks.
1717
"""
1818

19+
_exports = {
20+
"connections": {
21+
"format": "TODO",
22+
"description": "All circuit and copper wire connections",
23+
"required": lambda x: len(x) != 0,
24+
}
25+
}
26+
1927
def __init__(self, name, similar_entities, **kwargs):
2028
# type: (str, list[str], **dict) -> None
2129
super(CircuitConnectableMixin, self).__init__(name, similar_entities, **kwargs)
@@ -39,7 +47,7 @@ def __init__(self, name, similar_entities, **kwargs):
3947
if "connections" in kwargs:
4048
self.connections = kwargs["connections"]
4149
self.unused_args.pop("connections")
42-
self._add_export("connections", lambda x: len(x) != 0)
50+
# self._add_export("connections", lambda x: len(x) != 0)
4351

4452
# =========================================================================
4553

draftsman/classes/mixins/circuit_read_contents.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class CircuitReadContentsMixin(object): # (ControlBehaviorMixin)
1818
| :py:class:`~draftsman.classes.mixins.circuit_read_resource.CircuitReadResourceMixin`
1919
"""
2020

21+
_exports = {}
22+
2123
@property
2224
def read_contents(self):
2325
# type: () -> bool

draftsman/classes/mixins/circuit_read_hand.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class CircuitReadHandMixin(object): # (ControlBehaviorMixin)
1818
| :py:class:`~draftsman.classes.mixins.circuit_read_resource.CircuitReadResourceMixin`
1919
"""
2020

21+
_exports = {}
22+
2123
@property
2224
def read_hand_contents(self):
2325
# type: () -> bool

0 commit comments

Comments
 (0)