From c4d9e66976e0e4db982f07e46045f9ca3ad559a8 Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Tue, 28 Dec 2021 14:08:50 +0100 Subject: [PATCH 01/11] Nets and Primitives Refactoring Now Primitives and Nets from EDB are casted into a new data object. They both inherit the original EDB object so the functionalities and back compatibility is preserved. --- pyaedt/edb_core/EDB_Data.py | 431 +++++++++++++++++++++++++++++++++++- pyaedt/edb_core/layout.py | 16 +- pyaedt/edb_core/nets.py | 29 ++- 3 files changed, 453 insertions(+), 23 deletions(-) diff --git a/pyaedt/edb_core/EDB_Data.py b/pyaedt/edb_core/EDB_Data.py index d8bf6019c98..935962a3184 100644 --- a/pyaedt/edb_core/EDB_Data.py +++ b/pyaedt/edb_core/EDB_Data.py @@ -1,9 +1,11 @@ import time import warnings +import math + from pyaedt.generic.general_methods import aedt_exception_handler, is_ironpython from pyaedt.edb_core.general import convert_py_list_to_net_list - +from pyaedt.modeler.GeometryOperators import GeometryOperators try: from System import Array from System.Collections.Generic import List @@ -13,6 +15,433 @@ ) +class EDBNetsData(object): + """Manages EDB functionalities for a primitives. + It Inherits EDB Object properties. + + Examples + -------- + >>> from pyaedt import Edb + >>> edb = Edb(myedb, edbversion="2021.2") + >>> edb_net = edb.core_nets.nets["GND"] + >>> edb_net.name # Class Property + >>> edb_net.GetName() # EDB Object Property + """ + + def __getattr__(self, key): + if key in dir(self): + try: + return getattr(self, key) + except: + raise AttributeError("Attribute not present") + + if key in dir(self.net_object): + try: + return getattr(self.net_object, key) + except: + raise AttributeError("Attribute not present") + + # def __setattr__(self, key, value): + # if key in dir(self): + # try: + # return setattr(self, key, value) + # except: + # raise AttributeError("Attribute not present") + # else: + # try: + # return setattr(self.rimitive, key, value) + # except: + # raise AttributeError("Attribute not present") + + def __init__(self, raw_net, core_app): + self._app = core_app + self._core_components = core_app.core_components + self._core_primitive = core_app.core_primitives + self.net_object = raw_net + + @property + def name(self): + """Return the Net Name. + + Returns + ------- + str + """ + return self.net_object.GetName() + + + @name.setter + def name(self, val): + self.net_object.SetName(val) + + @property + def primitives(self): + """Return the list of primitives that belongs to the net. + + Returns + ------- + list of :class:`pyaedt.edb_core.EDB_Data.EDBPrimitives` + """ + prims = [] + for el in self._core_primitive.primitives: + if self.name == el.net_name: + prims.append(el) + return prims + + @property + def is_power_ground(self): + """Either to get/set boolean for power/ground net. + + Returns + ------- + bool + """ + return self.net_object.IsPowerGround() + + @is_power_ground.setter + def is_power_ground(self, val): + if isinstance(val, bool): + self.net_object.SetIsPowerGround(val) + else: + raise AttributeError("Value has to be a boolean.") + + @property + def components(self): + """Return the list of components that touch the net. + + Returns + ------- + dict[str, :class:`pyaedt.edb_core.EDB_Data.EDBComponent`] + """ + comps = {} + for el, val in self._core_components.components.items(): + if self.name in val.nets: + comps[el] = val + return comps + + @aedt_exception_handler + def plot( + self, layers=None, show_legend=True, save_plot=None, outline=None, size=(2000, 1000) + ): + """Plot a Net to Matplotlib 2D Chart. + + Parameters + ---------- + layers : str, list, optional + Name of the layers to include in the plot. If `None` all the signal layers will be considered. + show_legend : bool, optional + If `True` the legend is shown in the plot. (default) + If `False` the legend is not shown. + save_plot : str, optional + If `None` the plot will be shown. + If a file path is specified the plot will be saved to such file. + outline : list, optional + List of points of the outline to plot. + size : tuple, optional + Image size in pixel (width, height). + """ + + self._app.core_nets.plot(self.name, layers=layers, show_legend=show_legend, save_plot=save_plot, + outline=outline, size=size) + +class EDBPrimitives(object): + """Manages EDB functionalities for a primitives. + It Inherits EDB Object properties. + + Examples + -------- + >>> from pyaedt import Edb + >>> edb = Edb(myedb, edbversion="2021.2") + >>> edb_prim = edb.core_primitives.primitives[0] + >>> edb_prim.is_void # Class Property + >>> edb_prim.IsVoid() # EDB Object Property + """ + + def __getattr__(self, key): + if key in dir(self): + try: + return getattr(self, key) + except: + raise AttributeError("Attribute not present") + + if key in dir(self.primitive_object): + try: + return getattr(self.primitive_object, key) + except: + raise AttributeError("Attribute not present") + + # def __setattr__(self, key, value): + # if key in dir(self): + # try: + # return setattr(self, key, value) + # except: + # raise AttributeError("Attribute not present") + # else: + # try: + # return setattr(self.rimitive, key, value) + # except: + # raise AttributeError("Attribute not present") + + def __init__(self, raw_primitive, core_app): + self._app = core_app + self._core_stackup = core_app.core_stackup + self._core_net = core_app.core_nets + self.primitive_object = raw_primitive + + @property + def is_void(self): + """Either if the primitive is a void or not. + + Returns + ------- + bool + """ + return self.primitive_object.IsVoid() + + @staticmethod + def _eval_arc_points(p1, p2, h, n=6, tol=1e-12): + """Get the points of the arc + + Parameters + ---------- + p1 : list + Arc starting point. + p2 : list + Arc ending point. + h : float + Arc height. + n : int + Number of points to generate along the arc. + tol : float + Geometric tolerance. + + Returns + ------- + list + points generated along the arc. + """ + # fmt: off + if abs(h) < tol: + return [], [] + elif h > 0: + reverse = False + x1 = p1[0] + y1 = p1[1] + x2 = p2[0] + y2 = p2[1] + else: + reverse = True + x1 = p2[0] + y1 = p2[1] + x2 = p1[0] + y2 = p1[1] + h *= -1 + xa = (x2-x1) / 2 + ya = (y2-y1) / 2 + xo = x1 + xa + yo = y1 + ya + a = math.sqrt(xa**2 + ya**2) + if a < tol: + return [], [] + r = (a**2)/(2*h) + h/2 + if abs(r-a) < tol: + b = 0 + th = 2 * math.asin(1) # chord angle + else: + b = math.sqrt(r**2 - a**2) + th = 2 * math.asin(a/r) # chord angle + + # center of the circle + xc = xo + b*ya/a + yc = yo - b*xa/a + + alpha = math.atan2((y1-yc), (x1-xc)) + xr = [] + yr = [] + for i in range(n): + i += 1 + dth = (i/(n+1)) * th + xi = xc + r * math.cos(alpha-dth) + yi = yc + r * math.sin(alpha-dth) + xr.append(xi) + yr.append(yi) + + if reverse: + xr.reverse() + yr.reverse() + # fmt: on + return xr, yr + + def _get_points_for_plot(self, my_net_points, num): + """ + Get the points to be plot + """ + # fmt: off + x = [] + y = [] + for i, point in enumerate(my_net_points): + # point = my_net_points[i] + if not point.IsArc(): + x.append(point.X.ToDouble()) + y.append(point.Y.ToDouble()) + # i += 1 + else: + arc_h = point.GetArcHeight().ToDouble() + p1 = [my_net_points[i-1].X.ToDouble(), my_net_points[i-1].Y.ToDouble()] + if i+1 < len(my_net_points): + p2 = [my_net_points[i+1].X.ToDouble(), my_net_points[i+1].Y.ToDouble()] + else: + p2 = [my_net_points[0].X.ToDouble(), my_net_points[0].Y.ToDouble()] + x_arc, y_arc = self._eval_arc_points(p1, p2, arc_h, num) + x.extend(x_arc) + y.extend(y_arc) + # i += 1 + # fmt: on + return x, y + + @aedt_exception_handler + def points(self, arc_segments=6): + """Return the list of points with arcs converted to segments. + + Parameters + ---------- + arc_segments : int + Number of facets to convert an arc. Default is `6`. + + + Returns + ------- + list, list + x and y list of points. + """ + try: + my_net_points = list(self.primitive_object.GetPolygonData().Points) + xt, yt = self._get_points_for_plot(my_net_points, arc_segments) + if not xt: + return [] + x, y = GeometryOperators.orient_polygon(xt, yt, clockwise=True) + return x, y + except: + x = [] + y = [] + return x, y + + @property + def voids(self): + """Return a list of voids of the given primitive if any. + + Returns + ------- + list of :class:`pyaedt.edb_core.EDB_Data.EDBPrimitives` + """ + voids = [] + for void in self.primitive_object.Voids: + voids.append(EDBPrimitives(void, self._app)) + return voids + + @aedt_exception_handler + def points_with_arcs(self): + """Return a list of points in [x,y] coordinate. + For arc, y is infinite and the arc height is represented with x coordinate. + + Returns + ------- + list of list + list of [x,y] couple of data. + """ + points = [] + try: + my_net_points = list(self.primitive_object.GetPolygonData().Points) + for i, point in enumerate(my_net_points): + points.append([point.X.ToDouble(), point.Y.ToDouble()]) + return points + except: + points = [] + return points + + @aedt_exception_handler + def is_arc(self, point): + """Either if a point is an arc or not. + + Returns + ------- + bool + """ + return not point.IsArc() + + @property + def type(self): + """Return the type of the primitive. + Allowed outputs are `"Circle"`, `"Rectangle"`,`"Polygon"`,`"Path"`,`"Bondwire"`. + + Returns + ------- + str + """ + types = ["Circle", "Path", "Polygon", "Rectangle", "Bondwire"] + str_type = self.primitive_object.ToString().split(".") + if str_type[-1] in types: + return str_type[-1] + return None + + @property + def net(self): + """Return EDB Net Object.""" + return self.primitive_object.GetNet() + + @property + def net_name(self): + """Get or Set the primitive net name. + + Returns + ------- + str + """ + return self.net.GetName() + + @net_name.setter + def net_name(self, val): + if val in self._core_net.nets: + net = self._core_net.nets[val].net_object + self.primitive_object.SetNet(net) + elif not isinstance(val, str): + try: + self.primitive_object.SetNet(val) + except: + raise AttributeError("Value inserted not found. Input has to be layer name or net object.") + else: + raise AttributeError("Value inserted not found. Input has to be layer name or net object.") + + @property + def layer(self): + """Get the primitive edb layer object. + """ + return self.primitive_object.GetLayer() + + @property + def layer_name(self): + """Get or Set the primitive layer name. + + Returns + ------- + str + """ + return self.layer.GetName() + + @layer_name.setter + def layer_name(self, val): + if val in self._core_stackup.stackup_layers.layers: + lay = self._core_stackup.stackup_layers.layers[val]._layer + self.primitive_object.SetLayer(lay) + elif not isinstance(val, str): + try: + self.primitive_object.SetLayer(val) + except: + raise AttributeError("Value inserted not found. Input has to be layer name or layer object.") + else: + raise AttributeError("Value inserted not found. Input has to be layer name or layer object.") + + class EDBLayer(object): """Manages EDB functionalities for a layer. diff --git a/pyaedt/edb_core/layout.py b/pyaedt/edb_core/layout.py index 0b7560eedd1..b46049f0c5b 100644 --- a/pyaedt/edb_core/layout.py +++ b/pyaedt/edb_core/layout.py @@ -8,6 +8,7 @@ from pyaedt.edb_core.general import convert_py_list_to_net_list from pyaedt.generic.general_methods import aedt_exception_handler from pyaedt.generic.general_methods import is_ironpython +from pyaedt.edb_core.EDB_Data import EDBPrimitives try: from System import Tuple @@ -92,7 +93,8 @@ def update_primitives(self): layoutObjectInstances = layoutInstance.GetAllLayoutObjInstances() for el in layoutObjectInstances.Items: try: - self._prims.append(el.GetLayoutObj()) + #self._prims.append(el.GetLayoutObj()) + self._prims.append(EDBPrimitives(el.GetLayoutObj(), self._pedb)) except: pass for lay in self.layers: @@ -140,7 +142,7 @@ def rectangles(self): prims = [] for el in self.primitives: try: - if "Rectangle" in el.ToString(): + if "Rectangle" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -159,7 +161,7 @@ def circles(self): prims = [] for el in self.primitives: try: - if "Circle" in el.ToString(): + if "Circle" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -177,7 +179,7 @@ def paths(self): prims = [] for el in self.primitives: try: - if "Path" in el.ToString(): + if "Path" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -195,7 +197,7 @@ def bondwires(self): prims = [] for el in self.primitives: try: - if "Bondwire" in el.ToString(): + if "Bondwire" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -207,13 +209,13 @@ def polygons(self): Returns ------- - list + list of :class:`pyaedt.edb_core.EDB_Data.EDBPrimitives` List of polygons. """ prims = [] for el in self.primitives: try: - if "Polygon" in el.ToString(): + if "Polygon" in el.primitive_object.ToString(): prims.append(el) except: pass diff --git a/pyaedt/edb_core/nets.py b/pyaedt/edb_core/nets.py index 5d79c349fa1..3485afc59ba 100644 --- a/pyaedt/edb_core/nets.py +++ b/pyaedt/edb_core/nets.py @@ -7,6 +7,7 @@ from pyaedt.generic.general_methods import aedt_exception_handler, generate_unique_name from pyaedt.modeler.GeometryOperators import GeometryOperators from pyaedt.generic.constants import CSS4_COLORS +from pyaedt.edb_core.EDB_Data import EDBNetsData try: from matplotlib import pyplot as plt @@ -79,12 +80,12 @@ def nets(self): Returns ------- - dict + dict[str, :class:`pyaedt.edb_core.EDB_Data.EDBNets`] Dictionary of nets. """ nets = {} for net in self._active_layout.Nets: - nets[net.GetName()] = net + nets[net.GetName()] = EDBNetsData(net, self._pedb) return nets @property @@ -264,18 +265,18 @@ def plot( if isinstance(nets, str): nets = [nets] + for path in self._pedb.core_primitives.paths: - net_name = path.GetNet().GetName() - layer_name = path.GetLayer().GetName() + net_name = path.net_name + layer_name = path.layer_name if net_name in nets and layer_name in layers: - my_net_points = list(path.GetPolygonData().Points) - x, y = self._get_points_for_plot(my_net_points) + x, y = path.points() if not x: continue if not color_by_net: label = "Layer " + layer_name if label not in label_colors: - color = path.GetLayer().GetColor() + color = path.layer.GetColor() try: c = (color.Item1 / 255, color.Item2 / 255, color.Item3 / 255) label_colors[label] = c @@ -299,13 +300,12 @@ def plot( plt.fill(x, y, c=label_colors[label], alpha=0.4) for poly in self._pedb.core_primitives.polygons: - if poly.IsVoid(): + if poly.is_void: continue - net_name = poly.GetNet().GetName() - layer_name = poly.GetLayer().GetName() + net_name = poly.net_name + layer_name = poly.layer_name if net_name in nets and layer_name in layers: - my_net_points = list(poly.GetPolygonData().Points) - xt, yt = self._get_points_for_plot(my_net_points) + xt, yt = poly.points() if not xt: continue x, y = GeometryOperators.orient_polygon(xt, yt, clockwise=True) @@ -315,9 +315,8 @@ def plot( vertices.append((0, 0)) codes.append(Path.CLOSEPOLY) - for void in poly.Voids: - void_points = list(void.GetPolygonData().Points) - xvt, yvt = self._get_points_for_plot(void_points) + for void in poly.voids: + xvt, yvt = void.points() if xvt: xv, yv = GeometryOperators.orient_polygon(xvt, yvt, clockwise=False) tmpV = [(i, j) for i, j in zip(xv, yv)] From 86b82211c38334823438f8b83c3c4280283d9cfb Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Tue, 28 Dec 2021 14:13:28 +0100 Subject: [PATCH 02/11] Fixed Style checks --- pyaedt/edb_core/EDB_Data.py | 17 ++++++++--------- pyaedt/edb_core/layout.py | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/pyaedt/edb_core/EDB_Data.py b/pyaedt/edb_core/EDB_Data.py index 935962a3184..7b6a566cb63 100644 --- a/pyaedt/edb_core/EDB_Data.py +++ b/pyaedt/edb_core/EDB_Data.py @@ -6,6 +6,7 @@ from pyaedt.generic.general_methods import aedt_exception_handler, is_ironpython from pyaedt.edb_core.general import convert_py_list_to_net_list from pyaedt.modeler.GeometryOperators import GeometryOperators + try: from System import Array from System.Collections.Generic import List @@ -69,7 +70,6 @@ def name(self): """ return self.net_object.GetName() - @name.setter def name(self, val): self.net_object.SetName(val) @@ -120,9 +120,7 @@ def components(self): return comps @aedt_exception_handler - def plot( - self, layers=None, show_legend=True, save_plot=None, outline=None, size=(2000, 1000) - ): + def plot(self, layers=None, show_legend=True, save_plot=None, outline=None, size=(2000, 1000)): """Plot a Net to Matplotlib 2D Chart. Parameters @@ -141,8 +139,10 @@ def plot( Image size in pixel (width, height). """ - self._app.core_nets.plot(self.name, layers=layers, show_legend=show_legend, save_plot=save_plot, - outline=outline, size=size) + self._app.core_nets.plot( + self.name, layers=layers, show_legend=show_legend, save_plot=save_plot, outline=outline, size=size + ) + class EDBPrimitives(object): """Manages EDB functionalities for a primitives. @@ -353,7 +353,7 @@ def points_with_arcs(self): try: my_net_points = list(self.primitive_object.GetPolygonData().Points) for i, point in enumerate(my_net_points): - points.append([point.X.ToDouble(), point.Y.ToDouble()]) + points.append([point.X.ToDouble(), point.Y.ToDouble()]) return points except: points = [] @@ -414,8 +414,7 @@ def net_name(self, val): @property def layer(self): - """Get the primitive edb layer object. - """ + """Get the primitive edb layer object.""" return self.primitive_object.GetLayer() @property diff --git a/pyaedt/edb_core/layout.py b/pyaedt/edb_core/layout.py index b46049f0c5b..1ec60a9bf22 100644 --- a/pyaedt/edb_core/layout.py +++ b/pyaedt/edb_core/layout.py @@ -93,7 +93,7 @@ def update_primitives(self): layoutObjectInstances = layoutInstance.GetAllLayoutObjInstances() for el in layoutObjectInstances.Items: try: - #self._prims.append(el.GetLayoutObj()) + # self._prims.append(el.GetLayoutObj()) self._prims.append(EDBPrimitives(el.GetLayoutObj(), self._pedb)) except: pass From 63db1b235548b4c4a094ddcb0f02cc69a08ff530 Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Tue, 28 Dec 2021 14:08:50 +0100 Subject: [PATCH 03/11] Nets and Primitives Refactoring Now Primitives and Nets from EDB are casted into a new data object. They both inherit the original EDB object so the functionalities and back compatibility is preserved. --- pyaedt/edb_core/EDB_Data.py | 431 +++++++++++++++++++++++++++++++++++- pyaedt/edb_core/layout.py | 16 +- pyaedt/edb_core/nets.py | 29 ++- 3 files changed, 453 insertions(+), 23 deletions(-) diff --git a/pyaedt/edb_core/EDB_Data.py b/pyaedt/edb_core/EDB_Data.py index d8bf6019c98..935962a3184 100644 --- a/pyaedt/edb_core/EDB_Data.py +++ b/pyaedt/edb_core/EDB_Data.py @@ -1,9 +1,11 @@ import time import warnings +import math + from pyaedt.generic.general_methods import aedt_exception_handler, is_ironpython from pyaedt.edb_core.general import convert_py_list_to_net_list - +from pyaedt.modeler.GeometryOperators import GeometryOperators try: from System import Array from System.Collections.Generic import List @@ -13,6 +15,433 @@ ) +class EDBNetsData(object): + """Manages EDB functionalities for a primitives. + It Inherits EDB Object properties. + + Examples + -------- + >>> from pyaedt import Edb + >>> edb = Edb(myedb, edbversion="2021.2") + >>> edb_net = edb.core_nets.nets["GND"] + >>> edb_net.name # Class Property + >>> edb_net.GetName() # EDB Object Property + """ + + def __getattr__(self, key): + if key in dir(self): + try: + return getattr(self, key) + except: + raise AttributeError("Attribute not present") + + if key in dir(self.net_object): + try: + return getattr(self.net_object, key) + except: + raise AttributeError("Attribute not present") + + # def __setattr__(self, key, value): + # if key in dir(self): + # try: + # return setattr(self, key, value) + # except: + # raise AttributeError("Attribute not present") + # else: + # try: + # return setattr(self.rimitive, key, value) + # except: + # raise AttributeError("Attribute not present") + + def __init__(self, raw_net, core_app): + self._app = core_app + self._core_components = core_app.core_components + self._core_primitive = core_app.core_primitives + self.net_object = raw_net + + @property + def name(self): + """Return the Net Name. + + Returns + ------- + str + """ + return self.net_object.GetName() + + + @name.setter + def name(self, val): + self.net_object.SetName(val) + + @property + def primitives(self): + """Return the list of primitives that belongs to the net. + + Returns + ------- + list of :class:`pyaedt.edb_core.EDB_Data.EDBPrimitives` + """ + prims = [] + for el in self._core_primitive.primitives: + if self.name == el.net_name: + prims.append(el) + return prims + + @property + def is_power_ground(self): + """Either to get/set boolean for power/ground net. + + Returns + ------- + bool + """ + return self.net_object.IsPowerGround() + + @is_power_ground.setter + def is_power_ground(self, val): + if isinstance(val, bool): + self.net_object.SetIsPowerGround(val) + else: + raise AttributeError("Value has to be a boolean.") + + @property + def components(self): + """Return the list of components that touch the net. + + Returns + ------- + dict[str, :class:`pyaedt.edb_core.EDB_Data.EDBComponent`] + """ + comps = {} + for el, val in self._core_components.components.items(): + if self.name in val.nets: + comps[el] = val + return comps + + @aedt_exception_handler + def plot( + self, layers=None, show_legend=True, save_plot=None, outline=None, size=(2000, 1000) + ): + """Plot a Net to Matplotlib 2D Chart. + + Parameters + ---------- + layers : str, list, optional + Name of the layers to include in the plot. If `None` all the signal layers will be considered. + show_legend : bool, optional + If `True` the legend is shown in the plot. (default) + If `False` the legend is not shown. + save_plot : str, optional + If `None` the plot will be shown. + If a file path is specified the plot will be saved to such file. + outline : list, optional + List of points of the outline to plot. + size : tuple, optional + Image size in pixel (width, height). + """ + + self._app.core_nets.plot(self.name, layers=layers, show_legend=show_legend, save_plot=save_plot, + outline=outline, size=size) + +class EDBPrimitives(object): + """Manages EDB functionalities for a primitives. + It Inherits EDB Object properties. + + Examples + -------- + >>> from pyaedt import Edb + >>> edb = Edb(myedb, edbversion="2021.2") + >>> edb_prim = edb.core_primitives.primitives[0] + >>> edb_prim.is_void # Class Property + >>> edb_prim.IsVoid() # EDB Object Property + """ + + def __getattr__(self, key): + if key in dir(self): + try: + return getattr(self, key) + except: + raise AttributeError("Attribute not present") + + if key in dir(self.primitive_object): + try: + return getattr(self.primitive_object, key) + except: + raise AttributeError("Attribute not present") + + # def __setattr__(self, key, value): + # if key in dir(self): + # try: + # return setattr(self, key, value) + # except: + # raise AttributeError("Attribute not present") + # else: + # try: + # return setattr(self.rimitive, key, value) + # except: + # raise AttributeError("Attribute not present") + + def __init__(self, raw_primitive, core_app): + self._app = core_app + self._core_stackup = core_app.core_stackup + self._core_net = core_app.core_nets + self.primitive_object = raw_primitive + + @property + def is_void(self): + """Either if the primitive is a void or not. + + Returns + ------- + bool + """ + return self.primitive_object.IsVoid() + + @staticmethod + def _eval_arc_points(p1, p2, h, n=6, tol=1e-12): + """Get the points of the arc + + Parameters + ---------- + p1 : list + Arc starting point. + p2 : list + Arc ending point. + h : float + Arc height. + n : int + Number of points to generate along the arc. + tol : float + Geometric tolerance. + + Returns + ------- + list + points generated along the arc. + """ + # fmt: off + if abs(h) < tol: + return [], [] + elif h > 0: + reverse = False + x1 = p1[0] + y1 = p1[1] + x2 = p2[0] + y2 = p2[1] + else: + reverse = True + x1 = p2[0] + y1 = p2[1] + x2 = p1[0] + y2 = p1[1] + h *= -1 + xa = (x2-x1) / 2 + ya = (y2-y1) / 2 + xo = x1 + xa + yo = y1 + ya + a = math.sqrt(xa**2 + ya**2) + if a < tol: + return [], [] + r = (a**2)/(2*h) + h/2 + if abs(r-a) < tol: + b = 0 + th = 2 * math.asin(1) # chord angle + else: + b = math.sqrt(r**2 - a**2) + th = 2 * math.asin(a/r) # chord angle + + # center of the circle + xc = xo + b*ya/a + yc = yo - b*xa/a + + alpha = math.atan2((y1-yc), (x1-xc)) + xr = [] + yr = [] + for i in range(n): + i += 1 + dth = (i/(n+1)) * th + xi = xc + r * math.cos(alpha-dth) + yi = yc + r * math.sin(alpha-dth) + xr.append(xi) + yr.append(yi) + + if reverse: + xr.reverse() + yr.reverse() + # fmt: on + return xr, yr + + def _get_points_for_plot(self, my_net_points, num): + """ + Get the points to be plot + """ + # fmt: off + x = [] + y = [] + for i, point in enumerate(my_net_points): + # point = my_net_points[i] + if not point.IsArc(): + x.append(point.X.ToDouble()) + y.append(point.Y.ToDouble()) + # i += 1 + else: + arc_h = point.GetArcHeight().ToDouble() + p1 = [my_net_points[i-1].X.ToDouble(), my_net_points[i-1].Y.ToDouble()] + if i+1 < len(my_net_points): + p2 = [my_net_points[i+1].X.ToDouble(), my_net_points[i+1].Y.ToDouble()] + else: + p2 = [my_net_points[0].X.ToDouble(), my_net_points[0].Y.ToDouble()] + x_arc, y_arc = self._eval_arc_points(p1, p2, arc_h, num) + x.extend(x_arc) + y.extend(y_arc) + # i += 1 + # fmt: on + return x, y + + @aedt_exception_handler + def points(self, arc_segments=6): + """Return the list of points with arcs converted to segments. + + Parameters + ---------- + arc_segments : int + Number of facets to convert an arc. Default is `6`. + + + Returns + ------- + list, list + x and y list of points. + """ + try: + my_net_points = list(self.primitive_object.GetPolygonData().Points) + xt, yt = self._get_points_for_plot(my_net_points, arc_segments) + if not xt: + return [] + x, y = GeometryOperators.orient_polygon(xt, yt, clockwise=True) + return x, y + except: + x = [] + y = [] + return x, y + + @property + def voids(self): + """Return a list of voids of the given primitive if any. + + Returns + ------- + list of :class:`pyaedt.edb_core.EDB_Data.EDBPrimitives` + """ + voids = [] + for void in self.primitive_object.Voids: + voids.append(EDBPrimitives(void, self._app)) + return voids + + @aedt_exception_handler + def points_with_arcs(self): + """Return a list of points in [x,y] coordinate. + For arc, y is infinite and the arc height is represented with x coordinate. + + Returns + ------- + list of list + list of [x,y] couple of data. + """ + points = [] + try: + my_net_points = list(self.primitive_object.GetPolygonData().Points) + for i, point in enumerate(my_net_points): + points.append([point.X.ToDouble(), point.Y.ToDouble()]) + return points + except: + points = [] + return points + + @aedt_exception_handler + def is_arc(self, point): + """Either if a point is an arc or not. + + Returns + ------- + bool + """ + return not point.IsArc() + + @property + def type(self): + """Return the type of the primitive. + Allowed outputs are `"Circle"`, `"Rectangle"`,`"Polygon"`,`"Path"`,`"Bondwire"`. + + Returns + ------- + str + """ + types = ["Circle", "Path", "Polygon", "Rectangle", "Bondwire"] + str_type = self.primitive_object.ToString().split(".") + if str_type[-1] in types: + return str_type[-1] + return None + + @property + def net(self): + """Return EDB Net Object.""" + return self.primitive_object.GetNet() + + @property + def net_name(self): + """Get or Set the primitive net name. + + Returns + ------- + str + """ + return self.net.GetName() + + @net_name.setter + def net_name(self, val): + if val in self._core_net.nets: + net = self._core_net.nets[val].net_object + self.primitive_object.SetNet(net) + elif not isinstance(val, str): + try: + self.primitive_object.SetNet(val) + except: + raise AttributeError("Value inserted not found. Input has to be layer name or net object.") + else: + raise AttributeError("Value inserted not found. Input has to be layer name or net object.") + + @property + def layer(self): + """Get the primitive edb layer object. + """ + return self.primitive_object.GetLayer() + + @property + def layer_name(self): + """Get or Set the primitive layer name. + + Returns + ------- + str + """ + return self.layer.GetName() + + @layer_name.setter + def layer_name(self, val): + if val in self._core_stackup.stackup_layers.layers: + lay = self._core_stackup.stackup_layers.layers[val]._layer + self.primitive_object.SetLayer(lay) + elif not isinstance(val, str): + try: + self.primitive_object.SetLayer(val) + except: + raise AttributeError("Value inserted not found. Input has to be layer name or layer object.") + else: + raise AttributeError("Value inserted not found. Input has to be layer name or layer object.") + + class EDBLayer(object): """Manages EDB functionalities for a layer. diff --git a/pyaedt/edb_core/layout.py b/pyaedt/edb_core/layout.py index 0b7560eedd1..b46049f0c5b 100644 --- a/pyaedt/edb_core/layout.py +++ b/pyaedt/edb_core/layout.py @@ -8,6 +8,7 @@ from pyaedt.edb_core.general import convert_py_list_to_net_list from pyaedt.generic.general_methods import aedt_exception_handler from pyaedt.generic.general_methods import is_ironpython +from pyaedt.edb_core.EDB_Data import EDBPrimitives try: from System import Tuple @@ -92,7 +93,8 @@ def update_primitives(self): layoutObjectInstances = layoutInstance.GetAllLayoutObjInstances() for el in layoutObjectInstances.Items: try: - self._prims.append(el.GetLayoutObj()) + #self._prims.append(el.GetLayoutObj()) + self._prims.append(EDBPrimitives(el.GetLayoutObj(), self._pedb)) except: pass for lay in self.layers: @@ -140,7 +142,7 @@ def rectangles(self): prims = [] for el in self.primitives: try: - if "Rectangle" in el.ToString(): + if "Rectangle" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -159,7 +161,7 @@ def circles(self): prims = [] for el in self.primitives: try: - if "Circle" in el.ToString(): + if "Circle" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -177,7 +179,7 @@ def paths(self): prims = [] for el in self.primitives: try: - if "Path" in el.ToString(): + if "Path" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -195,7 +197,7 @@ def bondwires(self): prims = [] for el in self.primitives: try: - if "Bondwire" in el.ToString(): + if "Bondwire" in el.primitive_object.ToString(): prims.append(el) except: pass @@ -207,13 +209,13 @@ def polygons(self): Returns ------- - list + list of :class:`pyaedt.edb_core.EDB_Data.EDBPrimitives` List of polygons. """ prims = [] for el in self.primitives: try: - if "Polygon" in el.ToString(): + if "Polygon" in el.primitive_object.ToString(): prims.append(el) except: pass diff --git a/pyaedt/edb_core/nets.py b/pyaedt/edb_core/nets.py index 5d79c349fa1..3485afc59ba 100644 --- a/pyaedt/edb_core/nets.py +++ b/pyaedt/edb_core/nets.py @@ -7,6 +7,7 @@ from pyaedt.generic.general_methods import aedt_exception_handler, generate_unique_name from pyaedt.modeler.GeometryOperators import GeometryOperators from pyaedt.generic.constants import CSS4_COLORS +from pyaedt.edb_core.EDB_Data import EDBNetsData try: from matplotlib import pyplot as plt @@ -79,12 +80,12 @@ def nets(self): Returns ------- - dict + dict[str, :class:`pyaedt.edb_core.EDB_Data.EDBNets`] Dictionary of nets. """ nets = {} for net in self._active_layout.Nets: - nets[net.GetName()] = net + nets[net.GetName()] = EDBNetsData(net, self._pedb) return nets @property @@ -264,18 +265,18 @@ def plot( if isinstance(nets, str): nets = [nets] + for path in self._pedb.core_primitives.paths: - net_name = path.GetNet().GetName() - layer_name = path.GetLayer().GetName() + net_name = path.net_name + layer_name = path.layer_name if net_name in nets and layer_name in layers: - my_net_points = list(path.GetPolygonData().Points) - x, y = self._get_points_for_plot(my_net_points) + x, y = path.points() if not x: continue if not color_by_net: label = "Layer " + layer_name if label not in label_colors: - color = path.GetLayer().GetColor() + color = path.layer.GetColor() try: c = (color.Item1 / 255, color.Item2 / 255, color.Item3 / 255) label_colors[label] = c @@ -299,13 +300,12 @@ def plot( plt.fill(x, y, c=label_colors[label], alpha=0.4) for poly in self._pedb.core_primitives.polygons: - if poly.IsVoid(): + if poly.is_void: continue - net_name = poly.GetNet().GetName() - layer_name = poly.GetLayer().GetName() + net_name = poly.net_name + layer_name = poly.layer_name if net_name in nets and layer_name in layers: - my_net_points = list(poly.GetPolygonData().Points) - xt, yt = self._get_points_for_plot(my_net_points) + xt, yt = poly.points() if not xt: continue x, y = GeometryOperators.orient_polygon(xt, yt, clockwise=True) @@ -315,9 +315,8 @@ def plot( vertices.append((0, 0)) codes.append(Path.CLOSEPOLY) - for void in poly.Voids: - void_points = list(void.GetPolygonData().Points) - xvt, yvt = self._get_points_for_plot(void_points) + for void in poly.voids: + xvt, yvt = void.points() if xvt: xv, yv = GeometryOperators.orient_polygon(xvt, yvt, clockwise=False) tmpV = [(i, j) for i, j in zip(xv, yv)] From a8744cac9714c68858d57e5fe04660b00daff0db Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Tue, 28 Dec 2021 14:13:28 +0100 Subject: [PATCH 04/11] Fixed Style checks --- pyaedt/edb_core/EDB_Data.py | 17 ++++++++--------- pyaedt/edb_core/layout.py | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/pyaedt/edb_core/EDB_Data.py b/pyaedt/edb_core/EDB_Data.py index 935962a3184..7b6a566cb63 100644 --- a/pyaedt/edb_core/EDB_Data.py +++ b/pyaedt/edb_core/EDB_Data.py @@ -6,6 +6,7 @@ from pyaedt.generic.general_methods import aedt_exception_handler, is_ironpython from pyaedt.edb_core.general import convert_py_list_to_net_list from pyaedt.modeler.GeometryOperators import GeometryOperators + try: from System import Array from System.Collections.Generic import List @@ -69,7 +70,6 @@ def name(self): """ return self.net_object.GetName() - @name.setter def name(self, val): self.net_object.SetName(val) @@ -120,9 +120,7 @@ def components(self): return comps @aedt_exception_handler - def plot( - self, layers=None, show_legend=True, save_plot=None, outline=None, size=(2000, 1000) - ): + def plot(self, layers=None, show_legend=True, save_plot=None, outline=None, size=(2000, 1000)): """Plot a Net to Matplotlib 2D Chart. Parameters @@ -141,8 +139,10 @@ def plot( Image size in pixel (width, height). """ - self._app.core_nets.plot(self.name, layers=layers, show_legend=show_legend, save_plot=save_plot, - outline=outline, size=size) + self._app.core_nets.plot( + self.name, layers=layers, show_legend=show_legend, save_plot=save_plot, outline=outline, size=size + ) + class EDBPrimitives(object): """Manages EDB functionalities for a primitives. @@ -353,7 +353,7 @@ def points_with_arcs(self): try: my_net_points = list(self.primitive_object.GetPolygonData().Points) for i, point in enumerate(my_net_points): - points.append([point.X.ToDouble(), point.Y.ToDouble()]) + points.append([point.X.ToDouble(), point.Y.ToDouble()]) return points except: points = [] @@ -414,8 +414,7 @@ def net_name(self, val): @property def layer(self): - """Get the primitive edb layer object. - """ + """Get the primitive edb layer object.""" return self.primitive_object.GetLayer() @property diff --git a/pyaedt/edb_core/layout.py b/pyaedt/edb_core/layout.py index b46049f0c5b..1ec60a9bf22 100644 --- a/pyaedt/edb_core/layout.py +++ b/pyaedt/edb_core/layout.py @@ -93,7 +93,7 @@ def update_primitives(self): layoutObjectInstances = layoutInstance.GetAllLayoutObjInstances() for el in layoutObjectInstances.Items: try: - #self._prims.append(el.GetLayoutObj()) + # self._prims.append(el.GetLayoutObj()) self._prims.append(EDBPrimitives(el.GetLayoutObj(), self._pedb)) except: pass From 8e68b72572897c929516aa4a668c0ff45d5b89dc Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Tue, 28 Dec 2021 17:19:28 +0100 Subject: [PATCH 05/11] Fixed bug on matplotlib import in Ironpython --- pyaedt/edb_core/nets.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/pyaedt/edb_core/nets.py b/pyaedt/edb_core/nets.py index 3485afc59ba..a8a1c141710 100644 --- a/pyaedt/edb_core/nets.py +++ b/pyaedt/edb_core/nets.py @@ -9,16 +9,17 @@ from pyaedt.generic.constants import CSS4_COLORS from pyaedt.edb_core.EDB_Data import EDBNetsData -try: - from matplotlib import pyplot as plt - from matplotlib.path import Path - from matplotlib.patches import PathPatch -except ImportError: - if not is_ironpython: - warnings.warn( - "The Matplotlib module is required to run some functionalities.\n" "Install with \npip install matplotlib" - ) - pass +if not is_ironpython: + try: + from matplotlib import pyplot as plt + from matplotlib.path import Path + from matplotlib.patches import PathPatch + except ImportError: + if not is_ironpython: + mess = "The Matplotlib module is required to run some functionalities.\n" + mess += "Install with \npip install matplotlib" + warnings.warn(mess) + pass class EdbNets(object): From 8c54e48bd5c0646cae7d5bc37147628ebfec65f9 Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Wed, 29 Dec 2021 08:42:18 +0100 Subject: [PATCH 06/11] Committed suggestions --- pyaedt/edb_core/EDB_Data.py | 36 +++++------------------------------- pyaedt/edb_core/layout.py | 1 - 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/pyaedt/edb_core/EDB_Data.py b/pyaedt/edb_core/EDB_Data.py index 7b6a566cb63..e3ec44f7969 100644 --- a/pyaedt/edb_core/EDB_Data.py +++ b/pyaedt/edb_core/EDB_Data.py @@ -42,18 +42,6 @@ def __getattr__(self, key): except: raise AttributeError("Attribute not present") - # def __setattr__(self, key, value): - # if key in dir(self): - # try: - # return setattr(self, key, value) - # except: - # raise AttributeError("Attribute not present") - # else: - # try: - # return setattr(self.rimitive, key, value) - # except: - # raise AttributeError("Attribute not present") - def __init__(self, raw_net, core_app): self._app = core_app self._core_components = core_app.core_components @@ -170,18 +158,6 @@ def __getattr__(self, key): except: raise AttributeError("Attribute not present") - # def __setattr__(self, key, value): - # if key in dir(self): - # try: - # return setattr(self, key, value) - # except: - # raise AttributeError("Attribute not present") - # else: - # try: - # return setattr(self.rimitive, key, value) - # except: - # raise AttributeError("Attribute not present") - def __init__(self, raw_primitive, core_app): self._app = core_app self._core_stackup = core_app.core_stackup @@ -217,8 +193,8 @@ def _eval_arc_points(p1, p2, h, n=6, tol=1e-12): Returns ------- - list - points generated along the arc. + list, list + Points generated along the arc. """ # fmt: off if abs(h) < tol: @@ -274,14 +250,13 @@ def _eval_arc_points(p1, p2, h, n=6, tol=1e-12): def _get_points_for_plot(self, my_net_points, num): """ - Get the points to be plot + Get the points to be plotted. """ # fmt: off x = [] y = [] for i, point in enumerate(my_net_points): - # point = my_net_points[i] - if not point.IsArc(): + if not self.is_arc(point): x.append(point.X.ToDouble()) y.append(point.Y.ToDouble()) # i += 1 @@ -308,7 +283,6 @@ def points(self, arc_segments=6): arc_segments : int Number of facets to convert an arc. Default is `6`. - Returns ------- list, list @@ -367,7 +341,7 @@ def is_arc(self, point): ------- bool """ - return not point.IsArc() + return point.IsArc() @property def type(self): diff --git a/pyaedt/edb_core/layout.py b/pyaedt/edb_core/layout.py index 1ec60a9bf22..acbf17c76be 100644 --- a/pyaedt/edb_core/layout.py +++ b/pyaedt/edb_core/layout.py @@ -93,7 +93,6 @@ def update_primitives(self): layoutObjectInstances = layoutInstance.GetAllLayoutObjInstances() for el in layoutObjectInstances.Items: try: - # self._prims.append(el.GetLayoutObj()) self._prims.append(EDBPrimitives(el.GetLayoutObj(), self._pedb)) except: pass From 26c7238aa97f3eb7f44e5d9d93a72ceee1c0dd1e Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Wed, 29 Dec 2021 08:57:47 +0100 Subject: [PATCH 07/11] Committed suggestions --- .../ironpython/plumbum/cli/i18n.py | 58 ++++ .../ironpython/plumbum/fs/atomic.py | 320 ++++++++++++++++++ .../third_party/ironpython/rpyc/lib/compat.py | 214 ++++++++++++ 3 files changed, 592 insertions(+) create mode 100644 pyaedt/third_party/ironpython/plumbum/cli/i18n.py create mode 100644 pyaedt/third_party/ironpython/plumbum/fs/atomic.py create mode 100644 pyaedt/third_party/ironpython/rpyc/lib/compat.py diff --git a/pyaedt/third_party/ironpython/plumbum/cli/i18n.py b/pyaedt/third_party/ironpython/plumbum/cli/i18n.py new file mode 100644 index 00000000000..9098aa69762 --- /dev/null +++ b/pyaedt/third_party/ironpython/plumbum/cli/i18n.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +import locale + +# High performance method for English (no translation needed) +loc = locale.getlocale()[0] +if loc is None or loc.startswith("en"): + + class NullTranslation(object): + def gettext(self, str): + return str + + def ngettext(self, str1, strN, n): + if n == 1: + return str1.replace("{0}", str(n)) + else: + return strN.replace("{0}", str(n)) + + def get_translation_for(package_name): + return NullTranslation() + + +else: + import gettext + import os + + # If not installed with setuptools, this might not be available + try: + import pkg_resources + except ImportError: + pkg_resources = None + + try: + from typing import Callable, List, Tuple + except ImportError: + pass + + local_dir = os.path.basename(__file__) + + def get_translation_for(package_name): # type: (str) -> gettext.NullTranslations + """Find and return gettext translation for package + (Try to find folder manually if setuptools does not exist) + """ + + if "." in package_name: + package_name = ".".join(package_name.split(".")[:-1]) + localedir = None + + if pkg_resources is None: + mydir = os.path.join(local_dir, "i18n") + else: + mydir = pkg_resources.resource_filename(package_name, "i18n") + + for localedir in mydir, None: + localefile = gettext.find(package_name, localedir) + if localefile: + break + + return gettext.translation(package_name, localedir=localedir, fallback=True) diff --git a/pyaedt/third_party/ironpython/plumbum/fs/atomic.py b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py new file mode 100644 index 00000000000..8bbdbe5db80 --- /dev/null +++ b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py @@ -0,0 +1,320 @@ +# -*- coding: utf-8 -*- +""" +Atomic file operations +""" + +import atexit +import os +import sys +import threading +from contextlib import contextmanager + +from plumbum.lib import six +from plumbum.machines.local import local + +if not hasattr(threading, "get_ident"): + try: + import thread + except ImportError: + import _thread as thread + threading.get_ident = thread.get_ident + del thread + +try: + import fcntl +except ImportError: + import msvcrt + + try: + from pywintypes import error as WinError + from win32con import LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY + from win32file import OVERLAPPED, LockFileEx, UnlockFile + except ImportError: + raise ImportError("On Windows, we require Python for Windows Extensions (pywin32)") + + @contextmanager + def locked_file(fileno, blocking=True): + hndl = msvcrt.get_osfhandle(fileno) + try: + LockFileEx( + hndl, + LOCKFILE_EXCLUSIVE_LOCK | (0 if blocking else LOCKFILE_FAIL_IMMEDIATELY), + 0xFFFFFFFF, + 0xFFFFFFFF, + OVERLAPPED(), + ) + except WinError: + _, ex, _ = sys.exc_info() + raise WindowsError(*ex.args) + try: + yield + finally: + UnlockFile(hndl, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF) + + +else: + if hasattr(fcntl, "lockf"): + + @contextmanager + def locked_file(fileno, blocking=True): + fcntl.lockf(fileno, fcntl.LOCK_EX | (0 if blocking else fcntl.LOCK_NB)) + try: + yield + finally: + fcntl.lockf(fileno, fcntl.LOCK_UN) + + else: + + @contextmanager + def locked_file(fileno, blocking=True): + fcntl.flock(fileno, fcntl.LOCK_EX | (0 if blocking else fcntl.LOCK_NB)) + try: + yield + finally: + fcntl.flock(fileno, fcntl.LOCK_UN) + + +class AtomicFile(object): + """ + Atomic file operations implemented using file-system advisory locks (``flock`` on POSIX, + ``LockFile`` on Windows). + .. note:: + On Linux, the manpage says ``flock`` might have issues with NFS mounts. You should + take this into account. + .. versionadded:: 1.3 + """ + + CHUNK_SIZE = 32 * 1024 + + def __init__(self, filename, ignore_deletion=False): + self.path = local.path(filename) + self._ignore_deletion = ignore_deletion + self._thdlock = threading.Lock() + self._owned_by = None + self._fileobj = None + self.reopen() + + def __repr__(self): + return "".format(self.path) if self._fileobj else "" + + def __del__(self): + self.close() + + def __enter__(self): + return self + + def __exit__(self, t, v, tb): + self.close() + + def close(self): + if self._fileobj is not None: + self._fileobj.close() + self._fileobj = None + + def reopen(self): + """ + Close and reopen the file; useful when the file was deleted from the file system + by a different process + """ + self.close() + self._fileobj = os.fdopen(os.open(str(self.path), os.O_CREAT | os.O_RDWR, 384), "r+b", 0) + + @contextmanager + def locked(self, blocking=True): + """ + A context manager that locks the file; this function is reentrant by the thread currently + holding the lock. + :param blocking: if ``True``, the call will block until we can grab the file system lock. + if ``False``, the call may fail immediately with the underlying exception + (``IOError`` or ``WindowsError``) + """ + if self._owned_by == threading.get_ident(): + yield + return + with self._thdlock: + with locked_file(self._fileobj.fileno(), blocking): + if not self.path.exists() and not self._ignore_deletion: + raise ValueError("Atomic file removed from filesystem") + self._owned_by = threading.get_ident() + try: + yield + finally: + self._owned_by = None + + def delete(self): + """ + Atomically delete the file (holds the lock while doing it) + """ + with self.locked(): + self.path.delete() + + def _read_all(self): + self._fileobj.seek(0) + data = [] + while True: + buf = self._fileobj.read(self.CHUNK_SIZE) + data.append(buf) + if len(buf) < self.CHUNK_SIZE: + break + return six.b("").join(data) + + def read_atomic(self): + """Atomically read the entire file""" + with self.locked(): + return self._read_all() + + def read_shared(self): + """Read the file **without** holding the lock""" + return self._read_all() + + def write_atomic(self, data): + """Writes the given data atomically to the file. Note that it overwrites the entire file; + ``write_atomic("foo")`` followed by ``write_atomic("bar")`` will result in only ``"bar"``. + """ + with self.locked(): + self._fileobj.seek(0) + while data: + chunk = data[: self.CHUNK_SIZE] + self._fileobj.write(chunk) + data = data[len(chunk) :] + self._fileobj.flush() + self._fileobj.truncate() + + +class AtomicCounterFile(object): + """ + An atomic counter based on AtomicFile. Each time you call ``next()``, it will + atomically read and increment the counter's value, returning its previous value + Example:: + acf = AtomicCounterFile.open("/some/file") + print acf.next() # e.g., 7 + print acf.next() # 8 + print acf.next() # 9 + .. versionadded:: 1.3 + """ + + def __init__(self, atomicfile, initial=0): + """ + :param atomicfile: an :class:`AtomicFile ` instance + :param initial: the initial value (used when the first time the file is created) + """ + self.atomicfile = atomicfile + self.initial = initial + + def __enter__(self): + return self + + def __exit__(self, t, v, tb): + self.close() + + def close(self): + self.atomicfile.close() + + @classmethod + def open(cls, filename): + """ + Shortcut for ``AtomicCounterFile(AtomicFile(filename))`` + """ + return cls(AtomicFile(filename)) + + def reset(self, value=None): + """ + Reset the counter's value to the one given. If ``None``, it will default to the + initial value provided to the constructor + """ + if value is None: + value = self.initial + if not isinstance(value, six.integer_types): + raise TypeError("value must be an integer, not {!r}".format(type(value))) + self.atomicfile.write_atomic(str(value).encode("utf8")) + + def next(self): + """ + Read and increment the counter, returning its previous value + """ + with self.atomicfile.locked(): + curr = self.atomicfile.read_atomic().decode("utf8") + if not curr: + curr = self.initial + else: + curr = int(curr) + self.atomicfile.write_atomic(str(curr + 1).encode("utf8")) + return curr + + +class PidFileTaken(SystemExit): + """ + This exception is raised when PidFile.acquire fails to lock the pid file. Note that it + derives from ``SystemExit``, so unless explicitly handled, it will terminate the process + cleanly + """ + + def __init__(self, msg, pid): + SystemExit.__init__(self, msg) + self.pid = pid + + +class PidFile(object): + """ + A PID file is a file that's locked by some process from the moment it starts until it dies + (the OS will clear the lock when the process exits). It is used to prevent two instances + of the same process (normally a daemon) from running concurrently. The PID file holds its + process' PID, so you know who's holding it. + .. versionadded:: 1.3 + """ + + def __init__(self, filename): + self.atomicfile = AtomicFile(filename) + self._ctx = None + + def __enter__(self): + self.acquire() + + def __exit__(self, t, v, tb): + self.release() + + def __del__(self): + try: + self.release() + except Exception: + pass + + def close(self): + self.atomicfile.close() + + def acquire(self): + """ + Attempt to acquire the PID file. If it's already locked, raises + :class:`PidFileTaken `. You should normally acquire + the file as early as possible when the program starts + """ + if self._ctx is not None: + return + self._ctx = self.atomicfile.locked(blocking=False) + try: + self._ctx.__enter__() + except (IOError, OSError): + self._ctx = None + try: + pid = self.atomicfile.read_shared().strip().decode("utf8") + except (IOError, OSError): + pid = "Unknown" + raise PidFileTaken( + "PID file {!r} taken by process {}".format(self.atomicfile.path, pid), + pid, + ) + else: + self.atomicfile.write_atomic(str(os.getpid()).encode("utf8")) + atexit.register(self.release) + + def release(self): + """ + Release the PID file (should only happen when the program terminates) + """ + if self._ctx is None: + return + self.atomicfile.delete() + try: + self._ctx.__exit__(None, None, None) + finally: + self._ctx = None diff --git a/pyaedt/third_party/ironpython/rpyc/lib/compat.py b/pyaedt/third_party/ironpython/rpyc/lib/compat.py new file mode 100644 index 00000000000..515562ca933 --- /dev/null +++ b/pyaedt/third_party/ironpython/rpyc/lib/compat.py @@ -0,0 +1,214 @@ +""" +compatibility module for various versions of python (2.4/3+/jython) +and various platforms (posix/windows) +""" +import sys +import time + +is_py_3k = sys.version_info[0] >= 3 +is_py_gte38 = is_py_3k and (sys.version_info[1] >= 8) +is_py_gte37 = is_py_3k and (sys.version_info[1] >= 7) + + +if is_py_3k: + exec("execute = exec") + + def BYTES_LITERAL(text): + return bytes(text, "utf8") + + maxint = sys.maxsize +else: + exec( + """def execute(code, globals = None, locals = None): + exec code in globals, locals""" + ) + + def BYTES_LITERAL(text): + return text + + maxint = sys.maxint + +try: + from struct import Struct # noqa: F401 +except ImportError: + import struct + + class Struct(object): + __slots__ = ["format", "size"] + + def __init__(self, format): + self.format = format + self.size = struct.calcsize(format) + + def pack(self, *args): + return struct.pack(self.format, *args) + + def unpack(self, data): + return struct.unpack(self.format, data) + + +try: + from cStringIO import StringIO as BytesIO +except ImportError: + from io import BytesIO # noqa: F401 + +try: + next = next +except NameError: + + def next(iterator): + return iterator.next() + + +try: + import cPickle as pickle +except ImportError: + import pickle # noqa: F401 + +try: + callable = callable +except NameError: + + def callable(obj): + return hasattr(obj, "__call__") + + +try: + import select as select_module +except ImportError: + select_module = None + + def select(*args): + raise ImportError("select not supported on this platform") + + +else: + # jython + if hasattr(select_module, "cpython_compatible_select"): + from select import cpython_compatible_select as select + else: + from select import select + + +def get_exc_errno(exc): + if hasattr(exc, "errno"): + return exc.errno + else: + return exc[0] + + +if select_module: + select_error = select_module.error +else: + select_error = IOError + +if hasattr(select_module, "poll"): + + class PollingPoll(object): + def __init__(self): + self._poll = select_module.poll() + + def register(self, fd, mode): + flags = 0 + if "r" in mode: + flags |= select_module.POLLIN | select_module.POLLPRI + if "w" in mode: + flags |= select_module.POLLOUT + if "e" in mode: + flags |= select_module.POLLERR + if "h" in mode: + # POLLRDHUP is a linux only extension, not known to python, but nevertheless + # used and thus needed in the flags + POLLRDHUP = 0x2000 + flags |= select_module.POLLHUP | select_module.POLLNVAL | POLLRDHUP + self._poll.register(fd, flags) + + modify = register + + def unregister(self, fd): + self._poll.unregister(fd) + + def poll(self, timeout=None): + if timeout: + # the real poll takes milliseconds while we have seconds here + timeout = 1000 * timeout + events = self._poll.poll(timeout) + processed = [] + for fd, evt in events: + mask = "" + if evt & (select_module.POLLIN | select_module.POLLPRI): + mask += "r" + if evt & select_module.POLLOUT: + mask += "w" + if evt & select_module.POLLERR: + mask += "e" + if evt & select_module.POLLHUP: + mask += "h" + if evt & select_module.POLLNVAL: + mask += "n" + processed.append((fd, mask)) + return processed + + poll = PollingPoll +else: + + class SelectingPoll(object): + def __init__(self): + self.rlist = set() + self.wlist = set() + + def register(self, fd, mode): + if "r" in mode: + self.rlist.add(fd) + if "w" in mode: + self.wlist.add(fd) + + modify = register + + def unregister(self, fd): + self.rlist.discard(fd) + self.wlist.discard(fd) + + def poll(self, timeout=None): + if not self.rlist and not self.wlist: + time.sleep(timeout) + return [] # need to return an empty array in this case + else: + rl, wl, _ = select(self.rlist, self.wlist, (), timeout) + return [(fd, "r") for fd in rl] + [(fd, "w") for fd in wl] + + poll = SelectingPoll + + +# Simplified version from six.with_metaclass +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # dummy metaclass that replaces itself with the actual metaclass after + # one level of class instantiation: + class metaclass(type): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + return type.__new__(metaclass, "temporary_class", (), {}) + + +if sys.version_info >= (3, 3): + TimeoutError = TimeoutError # noqa: F821 +else: + + class TimeoutError(Exception): # noqa: F821 + pass + + +if sys.version_info >= (3, 2): + + def acquire_lock(lock, blocking, timeout): + if blocking and timeout.finite: + return lock.acquire(blocking, timeout.timeleft()) + return lock.acquire(blocking) + + +else: + + def acquire_lock(lock, blocking, timeout): + return lock.acquire(blocking) From da030fcbab4aa39d71da06602161cdc91f4bbfb8 Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Wed, 29 Dec 2021 09:00:02 +0100 Subject: [PATCH 08/11] Committed suggestions --- pyaedt/third_party/ironpython/plumbum/cli/i18n.py | 1 - pyaedt/third_party/ironpython/plumbum/fs/atomic.py | 1 - pyaedt/third_party/ironpython/rpyc/lib/compat.py | 2 -- 3 files changed, 4 deletions(-) diff --git a/pyaedt/third_party/ironpython/plumbum/cli/i18n.py b/pyaedt/third_party/ironpython/plumbum/cli/i18n.py index 9098aa69762..43e488935de 100644 --- a/pyaedt/third_party/ironpython/plumbum/cli/i18n.py +++ b/pyaedt/third_party/ironpython/plumbum/cli/i18n.py @@ -18,7 +18,6 @@ def ngettext(self, str1, strN, n): def get_translation_for(package_name): return NullTranslation() - else: import gettext import os diff --git a/pyaedt/third_party/ironpython/plumbum/fs/atomic.py b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py index 8bbdbe5db80..9a5753aa3ab 100644 --- a/pyaedt/third_party/ironpython/plumbum/fs/atomic.py +++ b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py @@ -51,7 +51,6 @@ def locked_file(fileno, blocking=True): finally: UnlockFile(hndl, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF) - else: if hasattr(fcntl, "lockf"): diff --git a/pyaedt/third_party/ironpython/rpyc/lib/compat.py b/pyaedt/third_party/ironpython/rpyc/lib/compat.py index 515562ca933..b9c12ef6732 100644 --- a/pyaedt/third_party/ironpython/rpyc/lib/compat.py +++ b/pyaedt/third_party/ironpython/rpyc/lib/compat.py @@ -81,7 +81,6 @@ def callable(obj): def select(*args): raise ImportError("select not supported on this platform") - else: # jython if hasattr(select_module, "cpython_compatible_select"): @@ -207,7 +206,6 @@ def acquire_lock(lock, blocking, timeout): return lock.acquire(blocking, timeout.timeleft()) return lock.acquire(blocking) - else: def acquire_lock(lock, blocking, timeout): From 4ca3f097cda5502c676d87c3f617bf4d23e7e3f4 Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Wed, 29 Dec 2021 09:12:00 +0100 Subject: [PATCH 09/11] Committed suggestions --- .../ironpython/plumbum/fs/atomic.py | 25 +++++++++++++--- .../third_party/ironpython/rpyc/lib/compat.py | 30 ++++--------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/pyaedt/third_party/ironpython/plumbum/fs/atomic.py b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py index 9a5753aa3ab..1a261928345 100644 --- a/pyaedt/third_party/ironpython/plumbum/fs/atomic.py +++ b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py @@ -30,7 +30,9 @@ from win32con import LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY from win32file import OVERLAPPED, LockFileEx, UnlockFile except ImportError: - raise ImportError("On Windows, we require Python for Windows Extensions (pywin32)") + raise ImportError( + "On Windows, we require Python for Windows Extensions (pywin32)" + ) @contextmanager def locked_file(fileno, blocking=True): @@ -38,7 +40,8 @@ def locked_file(fileno, blocking=True): try: LockFileEx( hndl, - LOCKFILE_EXCLUSIVE_LOCK | (0 if blocking else LOCKFILE_FAIL_IMMEDIATELY), + LOCKFILE_EXCLUSIVE_LOCK + | (0 if blocking else LOCKFILE_FAIL_IMMEDIATELY), 0xFFFFFFFF, 0xFFFFFFFF, OVERLAPPED(), @@ -51,6 +54,7 @@ def locked_file(fileno, blocking=True): finally: UnlockFile(hndl, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF) + else: if hasattr(fcntl, "lockf"): @@ -77,9 +81,11 @@ class AtomicFile(object): """ Atomic file operations implemented using file-system advisory locks (``flock`` on POSIX, ``LockFile`` on Windows). + .. note:: On Linux, the manpage says ``flock`` might have issues with NFS mounts. You should take this into account. + .. versionadded:: 1.3 """ @@ -94,7 +100,11 @@ def __init__(self, filename, ignore_deletion=False): self.reopen() def __repr__(self): - return "".format(self.path) if self._fileobj else "" + return ( + "".format(self.path) + if self._fileobj + else "" + ) def __del__(self): self.close() @@ -116,13 +126,16 @@ def reopen(self): by a different process """ self.close() - self._fileobj = os.fdopen(os.open(str(self.path), os.O_CREAT | os.O_RDWR, 384), "r+b", 0) + self._fileobj = os.fdopen( + os.open(str(self.path), os.O_CREAT | os.O_RDWR, 384), "r+b", 0 + ) @contextmanager def locked(self, blocking=True): """ A context manager that locks the file; this function is reentrant by the thread currently holding the lock. + :param blocking: if ``True``, the call will block until we can grab the file system lock. if ``False``, the call may fail immediately with the underlying exception (``IOError`` or ``WindowsError``) @@ -184,11 +197,14 @@ class AtomicCounterFile(object): """ An atomic counter based on AtomicFile. Each time you call ``next()``, it will atomically read and increment the counter's value, returning its previous value + Example:: + acf = AtomicCounterFile.open("/some/file") print acf.next() # e.g., 7 print acf.next() # 8 print acf.next() # 9 + .. versionadded:: 1.3 """ @@ -259,6 +275,7 @@ class PidFile(object): (the OS will clear the lock when the process exits). It is used to prevent two instances of the same process (normally a daemon) from running concurrently. The PID file holds its process' PID, so you know who's holding it. + .. versionadded:: 1.3 """ diff --git a/pyaedt/third_party/ironpython/rpyc/lib/compat.py b/pyaedt/third_party/ironpython/rpyc/lib/compat.py index b9c12ef6732..47e0ae5c887 100644 --- a/pyaedt/third_party/ironpython/rpyc/lib/compat.py +++ b/pyaedt/third_party/ironpython/rpyc/lib/compat.py @@ -5,7 +5,7 @@ import sys import time -is_py_3k = sys.version_info[0] >= 3 +is_py_3k = (sys.version_info[0] >= 3) is_py_gte38 = is_py_3k and (sys.version_info[1] >= 8) is_py_gte37 = is_py_3k and (sys.version_info[1] >= 7) @@ -15,17 +15,13 @@ def BYTES_LITERAL(text): return bytes(text, "utf8") - maxint = sys.maxsize else: - exec( - """def execute(code, globals = None, locals = None): - exec code in globals, locals""" - ) + exec("""def execute(code, globals = None, locals = None): + exec code in globals, locals""") def BYTES_LITERAL(text): return text - maxint = sys.maxint try: @@ -46,7 +42,6 @@ def pack(self, *args): def unpack(self, data): return struct.unpack(self.format, data) - try: from cStringIO import StringIO as BytesIO except ImportError: @@ -55,11 +50,9 @@ def unpack(self, data): try: next = next except NameError: - def next(iterator): return iterator.next() - try: import cPickle as pickle except ImportError: @@ -68,11 +61,9 @@ def next(iterator): try: callable = callable except NameError: - def callable(obj): return hasattr(obj, "__call__") - try: import select as select_module except ImportError: @@ -80,10 +71,9 @@ def callable(obj): def select(*args): raise ImportError("select not supported on this platform") - else: # jython - if hasattr(select_module, "cpython_compatible_select"): + if hasattr(select_module, 'cpython_compatible_select'): from select import cpython_compatible_select as select else: from select import select @@ -102,7 +92,6 @@ def get_exc_errno(exc): select_error = IOError if hasattr(select_module, "poll"): - class PollingPoll(object): def __init__(self): self._poll = select_module.poll() @@ -121,7 +110,6 @@ def register(self, fd, mode): POLLRDHUP = 0x2000 flags |= select_module.POLLHUP | select_module.POLLNVAL | POLLRDHUP self._poll.register(fd, flags) - modify = register def unregister(self, fd): @@ -150,7 +138,6 @@ def poll(self, timeout=None): poll = PollingPoll else: - class SelectingPoll(object): def __init__(self): self.rlist = set() @@ -161,7 +148,6 @@ def register(self, fd, mode): self.rlist.add(fd) if "w" in mode: self.wlist.add(fd) - modify = register def unregister(self, fd): @@ -187,26 +173,20 @@ def with_metaclass(meta, *bases): class metaclass(type): def __new__(cls, name, this_bases, d): return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) + return type.__new__(metaclass, 'temporary_class', (), {}) if sys.version_info >= (3, 3): TimeoutError = TimeoutError # noqa: F821 else: - class TimeoutError(Exception): # noqa: F821 pass - if sys.version_info >= (3, 2): - def acquire_lock(lock, blocking, timeout): if blocking and timeout.finite: return lock.acquire(blocking, timeout.timeleft()) return lock.acquire(blocking) - else: - def acquire_lock(lock, blocking, timeout): return lock.acquire(blocking) From 07eee2470e36e09bbfd2299a62f841827b42fd8e Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Wed, 29 Dec 2021 09:25:49 +0100 Subject: [PATCH 10/11] Committed suggestions --- .../third_party/ironpython/rpyc/lib/compat.py | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/pyaedt/third_party/ironpython/rpyc/lib/compat.py b/pyaedt/third_party/ironpython/rpyc/lib/compat.py index 47e0ae5c887..515562ca933 100644 --- a/pyaedt/third_party/ironpython/rpyc/lib/compat.py +++ b/pyaedt/third_party/ironpython/rpyc/lib/compat.py @@ -5,7 +5,7 @@ import sys import time -is_py_3k = (sys.version_info[0] >= 3) +is_py_3k = sys.version_info[0] >= 3 is_py_gte38 = is_py_3k and (sys.version_info[1] >= 8) is_py_gte37 = is_py_3k and (sys.version_info[1] >= 7) @@ -15,13 +15,17 @@ def BYTES_LITERAL(text): return bytes(text, "utf8") + maxint = sys.maxsize else: - exec("""def execute(code, globals = None, locals = None): - exec code in globals, locals""") + exec( + """def execute(code, globals = None, locals = None): + exec code in globals, locals""" + ) def BYTES_LITERAL(text): return text + maxint = sys.maxint try: @@ -42,6 +46,7 @@ def pack(self, *args): def unpack(self, data): return struct.unpack(self.format, data) + try: from cStringIO import StringIO as BytesIO except ImportError: @@ -50,9 +55,11 @@ def unpack(self, data): try: next = next except NameError: + def next(iterator): return iterator.next() + try: import cPickle as pickle except ImportError: @@ -61,9 +68,11 @@ def next(iterator): try: callable = callable except NameError: + def callable(obj): return hasattr(obj, "__call__") + try: import select as select_module except ImportError: @@ -71,9 +80,11 @@ def callable(obj): def select(*args): raise ImportError("select not supported on this platform") + + else: # jython - if hasattr(select_module, 'cpython_compatible_select'): + if hasattr(select_module, "cpython_compatible_select"): from select import cpython_compatible_select as select else: from select import select @@ -92,6 +103,7 @@ def get_exc_errno(exc): select_error = IOError if hasattr(select_module, "poll"): + class PollingPoll(object): def __init__(self): self._poll = select_module.poll() @@ -110,6 +122,7 @@ def register(self, fd, mode): POLLRDHUP = 0x2000 flags |= select_module.POLLHUP | select_module.POLLNVAL | POLLRDHUP self._poll.register(fd, flags) + modify = register def unregister(self, fd): @@ -138,6 +151,7 @@ def poll(self, timeout=None): poll = PollingPoll else: + class SelectingPoll(object): def __init__(self): self.rlist = set() @@ -148,6 +162,7 @@ def register(self, fd, mode): self.rlist.add(fd) if "w" in mode: self.wlist.add(fd) + modify = register def unregister(self, fd): @@ -173,20 +188,27 @@ def with_metaclass(meta, *bases): class metaclass(type): def __new__(cls, name, this_bases, d): return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) + + return type.__new__(metaclass, "temporary_class", (), {}) if sys.version_info >= (3, 3): TimeoutError = TimeoutError # noqa: F821 else: + class TimeoutError(Exception): # noqa: F821 pass + if sys.version_info >= (3, 2): + def acquire_lock(lock, blocking, timeout): if blocking and timeout.finite: return lock.acquire(blocking, timeout.timeleft()) return lock.acquire(blocking) + + else: + def acquire_lock(lock, blocking, timeout): return lock.acquire(blocking) From e934faab65daf9a9e20bf1e2162a9d703fa327d8 Mon Sep 17 00:00:00 2001 From: Massimo Capodiferro Date: Wed, 29 Dec 2021 09:28:25 +0100 Subject: [PATCH 11/11] Committed suggestions --- .../ironpython/plumbum/cli/i18n.py | 1 + .../ironpython/plumbum/fs/atomic.py | 24 ++++--------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/pyaedt/third_party/ironpython/plumbum/cli/i18n.py b/pyaedt/third_party/ironpython/plumbum/cli/i18n.py index 43e488935de..9098aa69762 100644 --- a/pyaedt/third_party/ironpython/plumbum/cli/i18n.py +++ b/pyaedt/third_party/ironpython/plumbum/cli/i18n.py @@ -18,6 +18,7 @@ def ngettext(self, str1, strN, n): def get_translation_for(package_name): return NullTranslation() + else: import gettext import os diff --git a/pyaedt/third_party/ironpython/plumbum/fs/atomic.py b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py index 1a261928345..8bbdbe5db80 100644 --- a/pyaedt/third_party/ironpython/plumbum/fs/atomic.py +++ b/pyaedt/third_party/ironpython/plumbum/fs/atomic.py @@ -30,9 +30,7 @@ from win32con import LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY from win32file import OVERLAPPED, LockFileEx, UnlockFile except ImportError: - raise ImportError( - "On Windows, we require Python for Windows Extensions (pywin32)" - ) + raise ImportError("On Windows, we require Python for Windows Extensions (pywin32)") @contextmanager def locked_file(fileno, blocking=True): @@ -40,8 +38,7 @@ def locked_file(fileno, blocking=True): try: LockFileEx( hndl, - LOCKFILE_EXCLUSIVE_LOCK - | (0 if blocking else LOCKFILE_FAIL_IMMEDIATELY), + LOCKFILE_EXCLUSIVE_LOCK | (0 if blocking else LOCKFILE_FAIL_IMMEDIATELY), 0xFFFFFFFF, 0xFFFFFFFF, OVERLAPPED(), @@ -81,11 +78,9 @@ class AtomicFile(object): """ Atomic file operations implemented using file-system advisory locks (``flock`` on POSIX, ``LockFile`` on Windows). - .. note:: On Linux, the manpage says ``flock`` might have issues with NFS mounts. You should take this into account. - .. versionadded:: 1.3 """ @@ -100,11 +95,7 @@ def __init__(self, filename, ignore_deletion=False): self.reopen() def __repr__(self): - return ( - "".format(self.path) - if self._fileobj - else "" - ) + return "".format(self.path) if self._fileobj else "" def __del__(self): self.close() @@ -126,16 +117,13 @@ def reopen(self): by a different process """ self.close() - self._fileobj = os.fdopen( - os.open(str(self.path), os.O_CREAT | os.O_RDWR, 384), "r+b", 0 - ) + self._fileobj = os.fdopen(os.open(str(self.path), os.O_CREAT | os.O_RDWR, 384), "r+b", 0) @contextmanager def locked(self, blocking=True): """ A context manager that locks the file; this function is reentrant by the thread currently holding the lock. - :param blocking: if ``True``, the call will block until we can grab the file system lock. if ``False``, the call may fail immediately with the underlying exception (``IOError`` or ``WindowsError``) @@ -197,14 +185,11 @@ class AtomicCounterFile(object): """ An atomic counter based on AtomicFile. Each time you call ``next()``, it will atomically read and increment the counter's value, returning its previous value - Example:: - acf = AtomicCounterFile.open("/some/file") print acf.next() # e.g., 7 print acf.next() # 8 print acf.next() # 9 - .. versionadded:: 1.3 """ @@ -275,7 +260,6 @@ class PidFile(object): (the OS will clear the lock when the process exits). It is used to prevent two instances of the same process (normally a daemon) from running concurrently. The PID file holds its process' PID, so you know who's holding it. - .. versionadded:: 1.3 """