diff options
Diffstat (limited to 'gerbonara/gerber/excellon_statements.py')
-rw-r--r-- | gerbonara/gerber/excellon_statements.py | 871 |
1 files changed, 0 insertions, 871 deletions
diff --git a/gerbonara/gerber/excellon_statements.py b/gerbonara/gerber/excellon_statements.py deleted file mode 100644 index 84e3bd3..0000000 --- a/gerbonara/gerber/excellon_statements.py +++ /dev/null @@ -1,871 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be> -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Excellon Statements -==================== -**Excellon file statement classes** - -""" - -import re -import uuid -import itertools -from enum import Enum -from .utils import (decimal_string, - inch, metric) - - -__all__ = ['ExcellonTool', 'ToolSelectionStmt', 'CoordinateStmt', - 'CommentStmt', 'HeaderBeginStmt', 'HeaderEndStmt', - 'RewindStopStmt', 'EndOfProgramStmt', 'UnitStmt', - 'IncrementalModeStmt', 'VersionStmt', 'FormatStmt', 'LinkToolStmt', - 'MeasuringModeStmt', 'RouteModeStmt', 'LinearModeStmt', 'DrillModeStmt', - 'AbsoluteModeStmt', 'RepeatHoleStmt', 'UnknownStmt', - 'ExcellonStatement', 'ZAxisRoutPositionStmt', - 'RetractWithClampingStmt', 'RetractWithoutClampingStmt', - 'CutterCompensationOffStmt', 'CutterCompensationLeftStmt', - 'CutterCompensationRightStmt', 'ZAxisInfeedRateStmt', - 'NextToolSelectionStmt', 'SlotStmt'] - - -class Plating(Enum): - UNKNOWN = 0 - NONPLATED = 1 - PLATED = 2 - OPTIONAL = 3 - -class ExcellonStatement: - pass - -class ExcellonTool(ExcellonStatement): - """ Excellon Tool class - - Parameters - ---------- - settings : FileSettings (dict-like) - File-wide settings. - - kwargs : dict-like - Tool settings from the excellon statement. Valid keys are: - - `diameter` : Tool diameter [expressed in file units] - - `rpm` : Tool RPM - - `feed_rate` : Z-axis tool feed rate - - `retract_rate` : Z-axis tool retraction rate - - `max_hit_count` : Number of hits allowed before a tool change - - `depth_offset` : Offset of tool depth from tip of tool. - - Attributes - ---------- - number : integer - Tool number from the excellon file - - diameter : float - Tool diameter in file units - - rpm : float - Tool RPM - - feed_rate : float - Tool Z-axis feed rate. - - retract_rate : float - Tool Z-axis retract rate - - depth_offset : float - Offset of depth measurement from tip of tool - - max_hit_count : integer - Maximum number of tool hits allowed before a tool change - - hit_count : integer - Number of tool hits in excellon file. - """ - - @classmethod - def from_dict(cls, settings, tool_dict): - """ Create an ExcellonTool from a dict. - - Parameters - ---------- - settings : FileSettings (dict-like) - Excellon File-wide settings - - tool_dict : dict - Excellon tool parameters as a dict - - Returns - ------- - tool : ExcellonTool - An ExcellonTool initialized with the parameters in tool_dict. - """ - return cls(settings, **tool_dict) - - def __init__(self, settings, **kwargs): - if kwargs.get('id') is not None: - super(ExcellonTool, self).__init__(id=kwargs.get('id')) - self.settings = settings - self.number = kwargs.get('number') - self.feed_rate = kwargs.get('feed_rate') - self.retract_rate = kwargs.get('retract_rate') - self.rpm = kwargs.get('rpm') - self.diameter = kwargs.get('diameter') - self.max_hit_count = kwargs.get('max_hit_count') - self.depth_offset = kwargs.get('depth_offset') - self.plated = kwargs.get('plated') - - self.hit_count = 0 - - def to_excellon(self, settings=None): - if self.settings and not settings: - settings = self.settings - stmt = 'T%02d' % self.number - if self.retract_rate is not None: - stmt += 'B%s' % settings.write_gerber_value(self.retract_rate) - if self.feed_rate is not None: - stmt += 'F%s' % settings.write_gerber_value(self.feed_rate) - if self.max_hit_count is not None: - stmt += 'H%s' % settings.write_gerber_value(self.max_hit_count) - if self.rpm is not None: - if self.rpm < 100000.: - stmt += 'S%s' % settings.write_gerber_value(self.rpm / 1000.) - else: - stmt += 'S%g' % (self.rpm / 1000.) - if self.diameter is not None: - stmt += 'C%s' % decimal_string(self.diameter, fmt[1], True) - if self.depth_offset is not None: - stmt += 'Z%s' % settings.write_gerber_value(self.depth_offset) - return stmt - - def to_inch(self): - if self.settings.units != 'inch': - self.settings.units = 'inch' - if self.diameter is not None: - self.diameter = inch(self.diameter) - - def to_metric(self): - if self.settings.units != 'metric': - self.settings.units = 'metric' - if self.diameter is not None: - self.diameter = metric(self.diameter) - - def _hit(self): - self.hit_count += 1 - - def equivalent(self, other): - """ - Is the other tool equal to this, ignoring the tool number, and other file specified properties - """ - - if type(self) != type(other): - return False - - return (self.diameter == other.diameter - and self.feed_rate == other.feed_rate - and self.retract_rate == other.retract_rate - and self.rpm == other.rpm - and self.depth_offset == other.depth_offset - and self.max_hit_count == other.max_hit_count - and self.plated == other.plated - and self.settings.units == other.settings.units) - - def __repr__(self): - unit = 'in.' if self.settings.units == 'inch' else 'mm' - fmtstr = '<ExcellonTool %%02d: %%%d.%dg%%s dia.>' % self.settings.format - return fmtstr % (self.number, self.diameter, unit) - - -class ToolSelectionStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - """ Create a ToolSelectionStmt from an excellon file line. - - Parameters - ---------- - line : string - Line from an Excellon file - - Returns - ------- - tool_statement : ToolSelectionStmt - ToolSelectionStmt representation of `line.` - """ - line = line[1:] - compensation_index = None - - # up to 3 characters for tool number (Frizting uses that) - if len(line) <= 3: - tool = int(line) - else: - tool = int(line[:2]) - compensation_index = int(line[2:]) - - return cls(tool, compensation_index, **kwargs) - - def __init__(self, tool, compensation_index=None, **kwargs): - super(ToolSelectionStmt, self).__init__(**kwargs) - tool = int(tool) - compensation_index = (int(compensation_index) if compensation_index - is not None else None) - self.tool = tool - self.compensation_index = compensation_index - - def to_excellon(self, settings=None): - stmt = 'T%02d' % self.tool - if self.compensation_index is not None: - stmt += '%02d' % self.compensation_index - return stmt - -class NextToolSelectionStmt(ExcellonStatement): - - # TODO the statement exists outside of the context of the file, - # so it is imposible to know that it is really the next tool - - def __init__(self, cur_tool, next_tool, **kwargs): - """ - Select the next tool in the wheel. - Parameters - ---------- - cur_tool : the tool that is currently selected - next_tool : the that that is now selected - """ - super(NextToolSelectionStmt, self).__init__(**kwargs) - - self.cur_tool = cur_tool - self.next_tool = next_tool - - def to_excellon(self, settings=None): - stmt = 'M00' - return stmt - -class ZAxisInfeedRateStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - """ Create a ZAxisInfeedRate from an excellon file line. - - Parameters - ---------- - line : string - Line from an Excellon file - - Returns - ------- - z_axis_infeed_rate : ToolSelectionStmt - ToolSelectionStmt representation of `line.` - """ - rate = int(line[1:]) - - return cls(rate, **kwargs) - - def __init__(self, rate, **kwargs): - super(ZAxisInfeedRateStmt, self).__init__(**kwargs) - self.rate = rate - - def to_excellon(self, settings=None): - return 'F%02d' % self.rate - - -class CoordinateStmt(ExcellonStatement): - - @classmethod - def from_point(cls, point, mode=None): - - stmt = cls(point[0], point[1]) - if mode: - stmt.mode = mode - return stmt - - @classmethod - def from_excellon(cls, line, settings, **kwargs): - x_coord = None - y_coord = None - if line[0] == 'X': - splitline = line.strip('X').split('Y') - x_coord = settings.parse_gerber_value(splitline[0]) - if len(splitline) == 2: - y_coord = settings.parse_gerber_value(splitline[1]) - else: - y_coord = settings.parse_gerber_value(line.strip(' Y')) - c = cls(x_coord, y_coord, **kwargs) - c.units = settings.units - return c - - def __init__(self, x=None, y=None, **kwargs): - super(CoordinateStmt, self).__init__(**kwargs) - self.x = x - self.y = y - self.mode = None - - def to_excellon(self, settings): - stmt = '' - if self.mode == "ROUT": - stmt += "G00" - if self.mode == "LINEAR": - stmt += "G01" - if self.x is not None: - stmt += 'X%s' % settings.write_gerber_value(self.x) - if self.y is not None: - stmt += 'Y%s' % settings.write_gerber_value(self.y) - return stmt - - def to_inch(self): - if self.units == 'metric': - self.units = 'inch' - if self.x is not None: - self.x = inch(self.x) - if self.y is not None: - self.y = inch(self.y) - - def to_metric(self): - if self.units == 'inch': - self.units = 'metric' - if self.x is not None: - self.x = metric(self.x) - if self.y is not None: - self.y = metric(self.y) - - def offset(self, x_offset=0, y_offset=0): - if self.x is not None: - self.x += x_offset - if self.y is not None: - self.y += y_offset - - def __str__(self): - coord_str = '' - if self.x is not None: - coord_str += 'X: %g ' % self.x - if self.y is not None: - coord_str += 'Y: %g ' % self.y - - return '<Coordinate Statement: %s>' % coord_str - - -class RepeatHoleStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, settings, **kwargs): - match = re.compile(r'R(?P<rcount>[0-9]*)X?(?P<xdelta>[+\-]?\d*\.?\d*)?Y?' - '(?P<ydelta>[+\-]?\d*\.?\d*)?').match(line) - stmt = match.groupdict() - count = int(stmt['rcount']) - xdelta = (settings.parse_gerber_value(stmt['xdelta']) - if stmt['xdelta'] is not '' else None) - ydelta = (settings.parse_gerber_value(stmt['ydelta']) - if stmt['ydelta'] is not '' else None) - c = cls(count, xdelta, ydelta, **kwargs) - c.units = settings.units - return c - - def __init__(self, count, xdelta=0.0, ydelta=0.0, **kwargs): - super(RepeatHoleStmt, self).__init__(**kwargs) - self.count = count - self.xdelta = xdelta - self.ydelta = ydelta - - def to_excellon(self, settings): - stmt = 'R%d' % self.count - if self.xdelta is not None and self.xdelta != 0.0: - stmt += 'X%s' % settings.write_gerber_value(self.xdelta) - if self.ydelta is not None and self.ydelta != 0.0: - stmt += 'Y%s' % settings.write_gerber_value(self.ydelta) - return stmt - - def to_inch(self): - if self.units == 'metric': - self.units = 'inch' - if self.xdelta is not None: - self.xdelta = inch(self.xdelta) - if self.ydelta is not None: - self.ydelta = inch(self.ydelta) - - def to_metric(self): - if self.units == 'inch': - self.units = 'metric' - if self.xdelta is not None: - self.xdelta = metric(self.xdelta) - if self.ydelta is not None: - self.ydelta = metric(self.ydelta) - - def __str__(self): - return '<Repeat Hole: %d times, offset X: %g Y: %g>' % ( - self.count, - self.xdelta if self.xdelta is not None else 0, - self.ydelta if self.ydelta is not None else 0) - - -class CommentStmt(ExcellonStatement): - - def __init__(self, comment): - self.comment = comment - - def to_excellon(self, settings=None): - return ';' + self.comment - - -class HeaderBeginStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(HeaderBeginStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'M48' - - -class HeaderEndStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(HeaderEndStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'M95' - - -class RewindStopStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(RewindStopStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return '%' - - -class ZAxisRoutPositionStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(ZAxisRoutPositionStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'M15' - - -class RetractWithClampingStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(RetractWithClampingStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'M16' - - -class RetractWithoutClampingStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(RetractWithoutClampingStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'M17' - - -class CutterCompensationOffStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(CutterCompensationOffStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'G40' - - -class CutterCompensationLeftStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(CutterCompensationLeftStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'G41' - - -class CutterCompensationRightStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(CutterCompensationRightStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'G42' - - -class EndOfProgramStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, settings, **kwargs): - match = re.compile(r'M30X?(?P<x>\d*\.?\d*)?Y?' - '(?P<y>\d*\.?\d*)?').match(line) - stmt = match.groupdict() - x = (settings.parse_gerber_value(stmt['x']) - if stmt['x'] is not '' else None) - y = (settings.parse_gerber_value(stmt['y']) - if stmt['y'] is not '' else None) - c = cls(x, y, **kwargs) - c.units = settings.units - return c - - def __init__(self, x=None, y=None, **kwargs): - super(EndOfProgramStmt, self).__init__(**kwargs) - self.x = x - self.y = y - - def to_excellon(self, settings): - stmt = 'M30' - if self.x is not None: - stmt += 'X%s' % settings.write_gerber_value(self.x) - if self.y is not None: - stmt += 'Y%s' % settings.write_gerber_value(self.y) - return stmt - - def to_inch(self): - if self.units == 'metric': - self.units = 'inch' - if self.x is not None: - self.x = inch(self.x) - if self.y is not None: - self.y = inch(self.y) - - def to_metric(self): - if self.units == 'inch': - self.units = 'metric' - if self.x is not None: - self.x = metric(self.x) - if self.y is not None: - self.y = metric(self.y) - - def offset(self, x_offset=0, y_offset=0): - if self.x is not None: - self.x += x_offset - if self.y is not None: - self.y += y_offset - - -class UnitStmt(ExcellonStatement): - - @classmethod - def from_settings(cls, settings): - """Create the unit statement from the FileSettings""" - - return cls(settings.units, settings.zeros) - - @classmethod - def from_excellon(cls, line, **kwargs): - units = 'inch' if 'INCH' in line else 'metric' - zeros = 'leading' if 'LZ' in line else 'trailing' - if '0000.00' in line: - format = (4, 2) - elif '000.000' in line: - format = (3, 3) - elif '00.0000' in line: - format = (2, 4) - else: - format = None - return cls(units, zeros, format, **kwargs) - - def __init__(self, units='inch', zeros='leading', format=None, **kwargs): - super(UnitStmt, self).__init__(**kwargs) - self.units = units.lower() - self.zeros = zeros - self.format = format - - def to_excellon(self, settings=None): - # TODO This won't export the invalid format statement if it exists - stmt = '%s,%s' % ('INCH' if self.units == 'inch' else 'METRIC', - 'LZ' if self.zeros == 'leading' - else 'TZ') - return stmt - - def to_inch(self): - self.units = 'inch' - - def to_metric(self): - self.units = 'metric' - - -class IncrementalModeStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - return cls('off', **kwargs) if 'OFF' in line else cls('on', **kwargs) - - def __init__(self, mode='off', **kwargs): - super(IncrementalModeStmt, self).__init__(**kwargs) - if mode.lower() not in ['on', 'off']: - raise ValueError('Mode may be "on" or "off"') - self.mode = mode - - def to_excellon(self, settings=None): - return 'ICI,%s' % ('OFF' if self.mode == 'off' else 'ON') - - -class VersionStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - version = int(line.split(',')[1]) - return cls(version, **kwargs) - - def __init__(self, version=1, **kwargs): - super(VersionStmt, self).__init__(**kwargs) - version = int(version) - if version not in [1, 2]: - raise ValueError('Valid versions are 1 or 2') - self.version = version - - def to_excellon(self, settings=None): - return 'VER,%d' % self.version - - -class FormatStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - fmt = int(line.split(',')[1]) - return cls(fmt, **kwargs) - - def __init__(self, format=1, **kwargs): - super(FormatStmt, self).__init__(**kwargs) - format = int(format) - if format not in [1, 2]: - raise ValueError('Valid formats are 1 or 2') - self.format = format - - def to_excellon(self, settings=None): - return 'FMAT,%d' % self.format - - @property - def format_tuple(self): - return (self.format, 6 - self.format) - - -class LinkToolStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - linked = [int(tool) for tool in line.split('/')] - return cls(linked, **kwargs) - - def __init__(self, linked_tools, **kwargs): - super(LinkToolStmt, self).__init__(**kwargs) - self.linked_tools = [int(x) for x in linked_tools] - - def to_excellon(self, settings=None): - return '/'.join([str(x) for x in self.linked_tools]) - - -class MeasuringModeStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - if not ('M71' in line or 'M72' in line): - raise ValueError('Not a measuring mode statement') - return cls('inch', **kwargs) if 'M72' in line else cls('metric', **kwargs) - - def __init__(self, units='inch', **kwargs): - super(MeasuringModeStmt, self).__init__(**kwargs) - units = units.lower() - if units not in ['inch', 'metric']: - raise ValueError('units must be "inch" or "metric"') - self.units = units - - def to_excellon(self, settings=None): - return 'M72' if self.units == 'inch' else 'M71' - - def to_inch(self): - self.units = 'inch' - - def to_metric(self): - self.units = 'metric' - - -class RouteModeStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(RouteModeStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'G00' - - -class LinearModeStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(LinearModeStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'G01' - - -class DrillModeStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(DrillModeStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'G05' - - -class AbsoluteModeStmt(ExcellonStatement): - - def __init__(self, **kwargs): - super(AbsoluteModeStmt, self).__init__(**kwargs) - - def to_excellon(self, settings=None): - return 'G90' - - -class UnknownStmt(ExcellonStatement): - - @classmethod - def from_excellon(cls, line, **kwargs): - return cls(line, **kwargs) - - def __init__(self, stmt, **kwargs): - super(UnknownStmt, self).__init__(**kwargs) - self.stmt = stmt - - def to_excellon(self, settings=None): - return self.stmt - - def __str__(self): - return "<Unknown Statement: %s>" % self.stmt - - -class SlotStmt(ExcellonStatement): - """ - G85 statement. Defines a slot created by multiple drills between two specified points. - - Format is two coordinates, split by G85in the middle, for example, XnY0nG85XnYn - """ - - @classmethod - def from_points(cls, start, end): - - return cls(start[0], start[1], end[0], end[1]) - - @classmethod - def from_excellon(cls, line, settings, **kwargs): - # Split the line based on the G85 separator - sub_coords = line.split('G85') - (x_start_coord, y_start_coord) = SlotStmt.parse_sub_coords(sub_coords[0], settings) - (x_end_coord, y_end_coord) = SlotStmt.parse_sub_coords(sub_coords[1], settings) - - # Some files seem to specify only one of the coordinates - if x_end_coord == None: - x_end_coord = x_start_coord - if y_end_coord == None: - y_end_coord = y_start_coord - - c = cls(x_start_coord, y_start_coord, x_end_coord, y_end_coord, **kwargs) - c.units = settings.units - return c - - @staticmethod - def parse_sub_coords(line, settings): - - x_coord = None - y_coord = None - - if line[0] == 'X': - splitline = line.strip('X').split('Y') - x_coord = settings.parse_gerber_value(splitline[0]) - if len(splitline) == 2: - y_coord = settings.parse_gerber_value(splitline[1]) - else: - y_coord = settings.parse_gerber_value(line.strip(' Y')) - - return (x_coord, y_coord) - - - def __init__(self, x_start=None, y_start=None, x_end=None, y_end=None, **kwargs): - super(SlotStmt, self).__init__(**kwargs) - self.x_start = x_start - self.y_start = y_start - self.x_end = x_end - self.y_end = y_end - self.mode = None - - def to_excellon(self, settings): - stmt = '' - - if self.x_start is not None: - stmt += 'X%s' % settings.write_gerber_value(self.x_start) - if self.y_start is not None: - stmt += 'Y%s' % settings.write_gerber_value(self.y_start) - - stmt += 'G85' - - if self.x_end is not None: - stmt += 'X%s' % settings.write_gerber_value(self.x_end) - if self.y_end is not None: - stmt += 'Y%s' % settings.write_gerber_value(self.y_end) - - return stmt - - def to_inch(self): - if self.units == 'metric': - self.units = 'inch' - if self.x_start is not None: - self.x_start = inch(self.x_start) - if self.y_start is not None: - self.y_start = inch(self.y_start) - if self.x_end is not None: - self.x_end = inch(self.x_end) - if self.y_end is not None: - self.y_end = inch(self.y_end) - - def to_metric(self): - if self.units == 'inch': - self.units = 'metric' - if self.x_start is not None: - self.x_start = metric(self.x_start) - if self.y_start is not None: - self.y_start = metric(self.y_start) - if self.x_end is not None: - self.x_end = metric(self.x_end) - if self.y_end is not None: - self.y_end = metric(self.y_end) - - def offset(self, x_offset=0, y_offset=0): - if self.x_start is not None: - self.x_start += x_offset - if self.y_start is not None: - self.y_start += y_offset - if self.x_end is not None: - self.x_end += x_offset - if self.y_end is not None: - self.y_end += y_offset - - def __str__(self): - start_str = '' - if self.x_start is not None: - start_str += 'X: %g ' % self.x_start - if self.y_start is not None: - start_str += 'Y: %g ' % self.y_start - - end_str = '' - if self.x_end is not None: - end_str += 'X: %g ' % self.x_end - if self.y_end is not None: - end_str += 'Y: %g ' % self.y_end - - return '<Slot Statement: %s to %s>' % (start_str, end_str) - -def pairwise(iterator): - """ Iterate over list taking two elements at a time. - - e.g. [1, 2, 3, 4, 5, 6] ==> [(1, 2), (3, 4), (5, 6)] - """ - a, b = itertools.tee(iterator) - itr = zip(itertools.islice(a, 0, None, 2), itertools.islice(b, 1, None, 2)) - for elem in itr: - yield elem |