mirror of
				https://github.com/python-escpos/python-escpos
				synced 2025-10-23 09:30:00 +00:00 
			
		
		
		
	Merge pull request #107 from DavisGoglin/improve-cli
Improve cli.py Complete rewrite of cli.py with configurable printer
This commit is contained in:
		
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -12,5 +12,10 @@ temp
 | 
				
			|||||||
# packaging and testing
 | 
					# packaging and testing
 | 
				
			||||||
.tox/
 | 
					.tox/
 | 
				
			||||||
*.egg-info/
 | 
					*.egg-info/
 | 
				
			||||||
 | 
					.eggs/
 | 
				
			||||||
build/
 | 
					build/
 | 
				
			||||||
dist/
 | 
					dist/
 | 
				
			||||||
 | 
					.coverage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# testing temporary directories
 | 
				
			||||||
 | 
					test/test-cli-output/
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								doc/api/config.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								doc/api/config.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					Config
 | 
				
			||||||
 | 
					---------
 | 
				
			||||||
 | 
					Module :py:mod:`escpos.config`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. automodule:: escpos.config
 | 
				
			||||||
 | 
					    :members:
 | 
				
			||||||
 | 
					    :inherited-members:
 | 
				
			||||||
 | 
					    :undoc-members:
 | 
				
			||||||
 | 
					    :show-inheritance:
 | 
				
			||||||
 | 
					    :member-order: bysource
 | 
				
			||||||
@@ -35,6 +35,7 @@ Content
 | 
				
			|||||||
   api/printer
 | 
					   api/printer
 | 
				
			||||||
   api/constants
 | 
					   api/constants
 | 
				
			||||||
   api/exceptions
 | 
					   api/exceptions
 | 
				
			||||||
 | 
					   api/config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Indices and tables
 | 
					Indices and tables
 | 
				
			||||||
==================
 | 
					==================
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -120,6 +120,63 @@ on USB interface
 | 
				
			|||||||
    # Cut paper
 | 
					    # Cut paper
 | 
				
			||||||
    Epson.cut()
 | 
					    Epson.cut()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Configuration File
 | 
				
			||||||
 | 
					------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can create a configuration file for python-escpos. This will
 | 
				
			||||||
 | 
					allow you to use the CLI, and skip some setup when using the library
 | 
				
			||||||
 | 
					programically.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The default configuration file is named ``config.yaml``. It's in the YAML
 | 
				
			||||||
 | 
					format. For windows it is probably at::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    %appdata%/python-escpos/config.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And for linux::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $HOME/.config/python-escpos/config.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you aren't sure, run::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from escpos import config
 | 
				
			||||||
 | 
					        c = config.Config()
 | 
				
			||||||
 | 
					        c.load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If it can't find the configuration file in the default location, it will tell
 | 
				
			||||||
 | 
					you where it's looking. You can always pass a path or a list of paths to
 | 
				
			||||||
 | 
					search to the ``load()`` method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To load the configured pritner, run::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from escpos import config
 | 
				
			||||||
 | 
					        c = config.Config()
 | 
				
			||||||
 | 
					        printer = c.printer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The printer section
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The ``printer`` configuration section defines a default printer to create.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The only required paramter is ``type``. The value of this should be one of the
 | 
				
			||||||
 | 
					printers defined in :doc:`/user/printers`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The rest of the parameters are whatever you want to pass to the printer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An example file printer::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        printer:
 | 
				
			||||||
 | 
					                type: File
 | 
				
			||||||
 | 
					                devfile: /dev/someprinter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And for a network printer::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        printer:
 | 
				
			||||||
 | 
					                type: network
 | 
				
			||||||
 | 
					                host: 127.0.0.1
 | 
				
			||||||
 | 
					                port: 9000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
How to update your code for USB printers
 | 
					How to update your code for USB printers
 | 
				
			||||||
----------------------------------------
 | 
					----------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										704
									
								
								escpos/cli.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										704
									
								
								escpos/cli.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@@ -1,213 +1,527 @@
 | 
				
			|||||||
#!/usr/bin/env python2
 | 
					#!/usr/bin/env python
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					""" CLI
 | 
				
			||||||
"""A simple command-line interface for common python-escpos functionality
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Usage: python -m escpos.cli --help
 | 
					This module acts as a command line interface for python-escpos. It mirrors
 | 
				
			||||||
 | 
					closely the available ESCPOS commands while adding a couple extra ones for convience.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Dependencies:
 | 
					It requires you to have a configuration file. See documentation for details.
 | 
				
			||||||
- DavisGoglin/python-escpos or better
 | 
					 | 
				
			||||||
- A file named weather.png (for the 'test' subcommand)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Reasons for using the DavisGoglin/python-escpos fork:
 | 
					 | 
				
			||||||
- image() accepts a PIL.Image object rather than requiring me to choose
 | 
					 | 
				
			||||||
  between writing a temporary file to disk or calling a "private" method.
 | 
					 | 
				
			||||||
- fullimage() allows me to print images of arbitrary length using slicing.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
How to print unsupported barcodes:
 | 
					 | 
				
			||||||
    barcode -b 'BARCODE' -e 'code39' -E | convert -density 200% eps:- code.png
 | 
					 | 
				
			||||||
    python test_escpos.py --images code.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Copyright (C) 2014 Stephan Sokolow (deitarion/SSokolow)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Permission is hereby granted, free of charge, to any person obtaining
 | 
					 | 
				
			||||||
a copy of this software and associated documentation files (the "Software"),
 | 
					 | 
				
			||||||
to deal in the Software without restriction, including without limitation
 | 
					 | 
				
			||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
					 | 
				
			||||||
and/or sell copies of the Software, and to permit persons to whom the
 | 
					 | 
				
			||||||
Software is furnished to do so, subject to the following conditions:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The above copyright notice and this permission notice shall be included
 | 
					 | 
				
			||||||
in all copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
					 | 
				
			||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 | 
					 | 
				
			||||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | 
					 | 
				
			||||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 | 
					 | 
				
			||||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 | 
					 | 
				
			||||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 | 
					 | 
				
			||||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
					 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from __future__ import absolute_import
 | 
					from __future__ import absolute_import
 | 
				
			||||||
 | 
					from __future__ import division
 | 
				
			||||||
__author__ = "Stephan Sokolow (deitarion/SSokolow)"
 | 
					from __future__ import print_function
 | 
				
			||||||
__license__ = "MIT"
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from escpos import printer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
epson = printer.Usb(0x0416, 0x5011)
 | 
					 | 
				
			||||||
# TODO: Un-hardcode this
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _print_text_file(path):
 | 
					 | 
				
			||||||
    """Print the given text file"""
 | 
					 | 
				
			||||||
    epson.set(align='left')
 | 
					 | 
				
			||||||
    with open(path, 'rU') as fobj:
 | 
					 | 
				
			||||||
        for line in fobj:
 | 
					 | 
				
			||||||
            epson.text(line)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _print_image_file(path):
 | 
					 | 
				
			||||||
    """Print the given image file."""
 | 
					 | 
				
			||||||
    epson.fullimage(path, histeq=False, width=384)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def print_files(args):
 | 
					 | 
				
			||||||
    """The 'print' subcommand"""
 | 
					 | 
				
			||||||
    for path in args.paths:
 | 
					 | 
				
			||||||
        if args.images:
 | 
					 | 
				
			||||||
            _print_image_file(path)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            _print_text_file(path)
 | 
					 | 
				
			||||||
    epson.cut()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# {{{ 'echo' Subcommand
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
KNOWN_BARCODE_TYPES = ['UPC-A', 'UPC-E', 'EAN13', 'ITF']
 | 
					 | 
				
			||||||
re_barcode_escape = re.compile(r'^%(?P<type>\S+)\s(?P<data>[0-9X]+)$')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def echo(args):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
    """TTY-like line-by-line keyboard-to-printer echo loop."""
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        while True:
 | 
					 | 
				
			||||||
            line = raw_input()
 | 
					 | 
				
			||||||
            match = re_barcode_escape.match(line)
 | 
					 | 
				
			||||||
            if match and match.group('type') in KNOWN_BARCODE_TYPES:
 | 
					 | 
				
			||||||
                bctype, data = match.groups()
 | 
					 | 
				
			||||||
                epson.barcode(data, bctype, 48, 2, '', '')
 | 
					 | 
				
			||||||
                epson.set(align='left')
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                epson.text('{0}\n'.format(line))
 | 
					 | 
				
			||||||
    except KeyboardInterrupt:
 | 
					 | 
				
			||||||
        epson.cut()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# }}}
 | 
					 | 
				
			||||||
# {{{ 'test' Subcommand
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from PIL import Image, ImageDraw
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _stall_test(width, height):
 | 
					 | 
				
			||||||
    """Generate a pattern to detect print glitches due to vertical stalling."""
 | 
					 | 
				
			||||||
    img = Image.new('1', (width, height))
 | 
					 | 
				
			||||||
    for pos in [(x, y) for y in range(0, height) for x in range(0, width)]:
 | 
					 | 
				
			||||||
        img.putpixel(pos, not sum(pos) % 10)
 | 
					 | 
				
			||||||
    return img
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _test_basic():
 | 
					 | 
				
			||||||
    """The original test code from python-escpos's Usage wiki page"""
 | 
					 | 
				
			||||||
    epson.set(align='left')
 | 
					 | 
				
			||||||
    # Print text
 | 
					 | 
				
			||||||
    epson.text("TODO:\n")  # pylint: disable=fixme
 | 
					 | 
				
			||||||
    epson.text("[ ] Task 1\n")
 | 
					 | 
				
			||||||
    epson.text("[ ] Task 2\n")
 | 
					 | 
				
			||||||
    # Print image
 | 
					 | 
				
			||||||
    # TODO: Bundle an image so this can be used
 | 
					 | 
				
			||||||
    # epson.image("weather.png")
 | 
					 | 
				
			||||||
    # Print QR Code (must have a white border to be scanned)
 | 
					 | 
				
			||||||
    epson.set(align='center')
 | 
					 | 
				
			||||||
    epson.text("Scan to recall TODO list")  # pylint: disable=fixme
 | 
					 | 
				
			||||||
    epson.qr("http://www.example.com/")
 | 
					 | 
				
			||||||
    # Print barcode
 | 
					 | 
				
			||||||
    epson.barcode('1234567890128', 'EAN13', 32, 2, '', '')
 | 
					 | 
				
			||||||
    # Cut paper
 | 
					 | 
				
			||||||
    epson.cut()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _test_barcodes():
 | 
					 | 
				
			||||||
    """Print test barcodes for all ESCPOS-specified formats."""
 | 
					 | 
				
			||||||
    for name, data in (
 | 
					 | 
				
			||||||
            # pylint: disable=bad-continuation
 | 
					 | 
				
			||||||
            ('UPC-A', '123456789012\x00'),
 | 
					 | 
				
			||||||
            ('UPC-E', '02345036\x00'),
 | 
					 | 
				
			||||||
            ('EAN13', '1234567890128\x00'),
 | 
					 | 
				
			||||||
            ('EAN8', '12345670\x00'),
 | 
					 | 
				
			||||||
            ('CODE39', 'BARCODE12345678\x00'),
 | 
					 | 
				
			||||||
            ('ITF', '123456\x00'),
 | 
					 | 
				
			||||||
            ('CODABAR', 'A40156B'),
 | 
					 | 
				
			||||||
            # TODO: CODE93 and CODE128
 | 
					 | 
				
			||||||
    ):
 | 
					 | 
				
			||||||
        # TODO: Fix the library to restore old alignment somehow
 | 
					 | 
				
			||||||
        epson.set(align='center')
 | 
					 | 
				
			||||||
        epson.text('\n{0}\n'.format(name))
 | 
					 | 
				
			||||||
        epson.barcode(data, name, 64, 2, '', '')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _test_patterns(width=384, height=255):
 | 
					 | 
				
			||||||
    """Print a set of test patterns for raster image output."""
 | 
					 | 
				
			||||||
    # Test our guess of the paper width
 | 
					 | 
				
			||||||
    img = Image.new('1', (width, height), color=1)
 | 
					 | 
				
			||||||
    draw = ImageDraw.Draw(img)
 | 
					 | 
				
			||||||
    draw.polygon(((0, 0), img.size, (0, img.size[1])), fill=0)
 | 
					 | 
				
			||||||
    epson.image(img)
 | 
					 | 
				
			||||||
    del draw, img
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Test the consistency of printing large data and whether stall rate is
 | 
					 | 
				
			||||||
    # affected by data rate
 | 
					 | 
				
			||||||
    epson.image(_stall_test(width, height))
 | 
					 | 
				
			||||||
    epson.image(_stall_test(width / 2, height))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test(args):
 | 
					 | 
				
			||||||
    """The 'test' subcommand"""
 | 
					 | 
				
			||||||
    if args.barcodes:
 | 
					 | 
				
			||||||
        _test_barcodes()
 | 
					 | 
				
			||||||
    elif args.patterns:
 | 
					 | 
				
			||||||
        _test_patterns()
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
        _test_basic()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# }}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main():
 | 
					 | 
				
			||||||
    """Wrapped in a function for import and entry point compatibility"""
 | 
					 | 
				
			||||||
    # pylint: disable=bad-continuation
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import argparse
 | 
					import argparse
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import six
 | 
				
			||||||
 | 
					from . import config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Must be defined before it's used in DEMO_FUNCTIONS
 | 
				
			||||||
 | 
					def str_to_bool(string):
 | 
				
			||||||
 | 
					    """ Used as a type in argparse so that we get back a proper
 | 
				
			||||||
 | 
					    bool instead of always True
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    return string.lower() in ('y', 'yes', '1', 'true')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# A list of functions that work better with a newline to be sent after them.
 | 
				
			||||||
 | 
					REQUIRES_NEWLINE = ('qr', 'barcode', 'text', 'block_text')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Used in demo method
 | 
				
			||||||
 | 
					# Key: The name of escpos function and the argument passed on the CLI. Some
 | 
				
			||||||
 | 
					#   manual translation is done in the case of barcodes_a -> barcode.
 | 
				
			||||||
 | 
					# Value: A list of dictionaries to pass to the escpos function as arguments.
 | 
				
			||||||
 | 
					DEMO_FUNCTIONS = {
 | 
				
			||||||
 | 
					    'text': [
 | 
				
			||||||
 | 
					        {'txt': 'Hello, World!\n',}
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    'qr': [
 | 
				
			||||||
 | 
					        {'text': 'This tests a QR code'},
 | 
				
			||||||
 | 
					        {'text': 'https://en.wikipedia.org/'}
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    'barcodes_a': [
 | 
				
			||||||
 | 
					        {'bc': 'UPC-A', 'code': '13243546576'},
 | 
				
			||||||
 | 
					        {'bc': 'UPC-E', 'code': '132435'},
 | 
				
			||||||
 | 
					        {'bc': 'EAN13', 'code': '1324354657687'},
 | 
				
			||||||
 | 
					        {'bc': 'EAN8', 'code': '1324354'},
 | 
				
			||||||
 | 
					        {'bc': 'CODE39', 'code': 'TEST'},
 | 
				
			||||||
 | 
					        {'bc': 'ITF', 'code': '55867492279103'},
 | 
				
			||||||
 | 
					        {'bc': 'NW7', 'code': 'A00000000A'},
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    'barcodes_b': [
 | 
				
			||||||
 | 
					        {'bc': 'UPC-A', 'code': '13243546576', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'UPC-E', 'code': '132435', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'EAN13', 'code': '1324354657687', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'EAN8', 'code': '1324354', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'CODE39', 'code': 'TEST', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'ITF', 'code': '55867492279103', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'NW7', 'code': 'A00000000A', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'CODE93', 'code': 'A00000000A', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'CODE93', 'code': '1324354657687', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'CODE128A', 'code': 'TEST', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'CODE128B', 'code': 'TEST', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'CODE128C', 'code': 'TEST', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'GS1-128', 'code': '00123456780000000001', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'GS1 DataBar Omnidirectional', 'code': '0000000000000', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'GS1 DataBar Truncated', 'code': '0000000000000', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'GS1 DataBar Limited', 'code': '0000000000000', 'function_type': 'B'},
 | 
				
			||||||
 | 
					        {'bc': 'GS1 DataBar Expanded', 'code': '00AAAAAAA', 'function_type': 'B'},
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Used to build the CLI
 | 
				
			||||||
 | 
					# A list of dictionaries. Each dict is a CLI argument.
 | 
				
			||||||
 | 
					# Keys:
 | 
				
			||||||
 | 
					# parser: A dict of args for command_parsers.add_parser
 | 
				
			||||||
 | 
					# defaults: A dict of args for subparser.set_defaults
 | 
				
			||||||
 | 
					# arguments: A list of dicts of args for subparser.add_argument
 | 
				
			||||||
 | 
					ESCPOS_COMMANDS = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'qr',
 | 
				
			||||||
 | 
					            'help': 'Print a QR code',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'qr',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--text',),
 | 
				
			||||||
 | 
					                'help': 'Text to print as a qr code',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'barcode',
 | 
				
			||||||
 | 
					            'help': 'Print a barcode',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'barcode',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--code',),
 | 
				
			||||||
 | 
					                'help': 'Barcode data to print',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--bc',),
 | 
				
			||||||
 | 
					                'help': 'Barcode format',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--height',),
 | 
				
			||||||
 | 
					                'help': 'Barcode height in px',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--width',),
 | 
				
			||||||
 | 
					                'help': 'Barcode width',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--pos',),
 | 
				
			||||||
 | 
					                'help': 'Label position',
 | 
				
			||||||
 | 
					                'choices': ['BELOW', 'ABOVE', 'BOTH', 'OFF'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--font',),
 | 
				
			||||||
 | 
					                'help': 'Label font',
 | 
				
			||||||
 | 
					                'choices': ['A', 'B'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--align_ct',),
 | 
				
			||||||
 | 
					                'help': 'Align barcode center',
 | 
				
			||||||
 | 
					                'type': str_to_bool,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--function_type',),
 | 
				
			||||||
 | 
					                'help': 'ESCPOS function type',
 | 
				
			||||||
 | 
					                'choices': ['A', 'B'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'text',
 | 
				
			||||||
 | 
					            'help': 'Print plain text',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'text',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--txt',),
 | 
				
			||||||
 | 
					                'help': 'Plain text to print',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'block_text',
 | 
				
			||||||
 | 
					            'help': 'Print wrapped text',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'block_text',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--txt',),
 | 
				
			||||||
 | 
					                'help': 'block_text to print',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--columns',),
 | 
				
			||||||
 | 
					                'help': 'Number of columns',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'cut',
 | 
				
			||||||
 | 
					            'help': 'Cut the paper',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'cut',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--mode',),
 | 
				
			||||||
 | 
					                'help': 'Type of cut',
 | 
				
			||||||
 | 
					                'choices': ['FULL', 'PART'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'cashdraw',
 | 
				
			||||||
 | 
					            'help': 'Kick the cash drawer',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'cashdraw',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--pin',),
 | 
				
			||||||
 | 
					                'help': 'Which PIN to kick',
 | 
				
			||||||
 | 
					                'choices': [2, 5],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'image',
 | 
				
			||||||
 | 
					            'help': 'Print an image',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'image',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--path_img',),
 | 
				
			||||||
 | 
					                'help': 'Path to image',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'fullimage',
 | 
				
			||||||
 | 
					            'help': 'Print a fullimage',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'fullimage',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--img',),
 | 
				
			||||||
 | 
					                'help': 'Path to img',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--max_height',),
 | 
				
			||||||
 | 
					                'help': 'Max height of image in px',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--width',),
 | 
				
			||||||
 | 
					                'help': 'Max width of image in px',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--histeq',),
 | 
				
			||||||
 | 
					                'help': 'Equalize the histrogram',
 | 
				
			||||||
 | 
					                'type': str_to_bool,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--bandsize',),
 | 
				
			||||||
 | 
					                'help': 'Size of bands to divide into when printing',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'charcode',
 | 
				
			||||||
 | 
					            'help': 'Set character code table',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'charcode',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--code',),
 | 
				
			||||||
 | 
					                'help': 'Character code',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'set',
 | 
				
			||||||
 | 
					            'help': 'Set text properties',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'set',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--align',),
 | 
				
			||||||
 | 
					                'help': 'Horizontal alignment',
 | 
				
			||||||
 | 
					                'choices': ['left', 'center', 'right'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--font',),
 | 
				
			||||||
 | 
					                'help': 'Font choice',
 | 
				
			||||||
 | 
					                'choices': ['left', 'center', 'right'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--text_type',),
 | 
				
			||||||
 | 
					                'help': 'Text properties',
 | 
				
			||||||
 | 
					                'choices': ['B', 'U', 'U2', 'BU', 'BU2', 'NORMAL'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--width',),
 | 
				
			||||||
 | 
					                'help': 'Width multiplier',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--height',),
 | 
				
			||||||
 | 
					                'help': 'Height multiplier',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--density',),
 | 
				
			||||||
 | 
					                'help': 'Print density',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--invert',),
 | 
				
			||||||
 | 
					                'help': 'White on black printing',
 | 
				
			||||||
 | 
					                'type': str_to_bool,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--smooth',),
 | 
				
			||||||
 | 
					                'help': 'Text smoothing. Effective on >:  4x4 text',
 | 
				
			||||||
 | 
					                'type': str_to_bool,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--flip',),
 | 
				
			||||||
 | 
					                'help': 'Text smoothing. Effective on >:  4x4 text',
 | 
				
			||||||
 | 
					                'type': str_to_bool,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'hw',
 | 
				
			||||||
 | 
					            'help': 'Hardware operations',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'hw',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--hw',),
 | 
				
			||||||
 | 
					                'help': 'Operation',
 | 
				
			||||||
 | 
					                'choices': ['INIT', 'SELECT', 'RESET'],
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'control',
 | 
				
			||||||
 | 
					            'help': 'Control sequences',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'control',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--ctl',),
 | 
				
			||||||
 | 
					                'help': 'Control sequence',
 | 
				
			||||||
 | 
					                'choices': ['LF', 'FF', 'CR', 'HT', 'VT'],
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--pos',),
 | 
				
			||||||
 | 
					                'help': 'Horizontal tab position (1-4)',
 | 
				
			||||||
 | 
					                'type': int,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'panel_buttons',
 | 
				
			||||||
 | 
					            'help': 'Controls panel buttons',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': 'panel_buttons',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--enable',),
 | 
				
			||||||
 | 
					                'help': 'Feed button enabled',
 | 
				
			||||||
 | 
					                'type': str_to_bool,
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'parser': {
 | 
				
			||||||
 | 
					            'name': 'raw',
 | 
				
			||||||
 | 
					            'help': 'Raw data',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'defaults': {
 | 
				
			||||||
 | 
					            'func': '_raw',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'arguments': [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'option_strings': ('--msg',),
 | 
				
			||||||
 | 
					                'help': 'Raw data to send',
 | 
				
			||||||
 | 
					                'required': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Handles loading of configuration and creating and processing of command
 | 
				
			||||||
 | 
					    line arguments. Called when run from a CLI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser = argparse.ArgumentParser(
 | 
					    parser = argparse.ArgumentParser(
 | 
				
			||||||
        description="Command-line interface to python-escpos")
 | 
					        description='CLI for python-escpos',
 | 
				
			||||||
    subparsers = parser.add_subparsers(title='subcommands')
 | 
					        epilog='Printer configuration is defined in the python-escpos config'
 | 
				
			||||||
 | 
					        'file. See documentation for details.',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    echo_parser = subparsers.add_parser('echo', help='Echo the keyboard to '
 | 
					    parser.register('type', 'bool', str_to_bool)
 | 
				
			||||||
                                                     'the printer line-by-line (Exit with Ctrl+C)')
 | 
					 | 
				
			||||||
    echo_parser.set_defaults(func=echo)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print_parser = subparsers.add_parser('print', help='Print the given files')
 | 
					    # Allow config file location to be passed
 | 
				
			||||||
    print_parser.add_argument('--images', action='store_true',
 | 
					    parser.add_argument(
 | 
				
			||||||
                              help="Provided files are images rather than text files.")
 | 
					        '-c', '--config',
 | 
				
			||||||
    print_parser.add_argument('paths', metavar='path', nargs='+')
 | 
					        help='Altnerate path to the configuration file',
 | 
				
			||||||
    print_parser.set_defaults(func=print_files)
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test_parser = subparsers.add_parser('test', help='Print test patterns')
 | 
					    # Everything interesting runs off of a subparser so we can use the format
 | 
				
			||||||
    test_modes = test_parser.add_mutually_exclusive_group()
 | 
					    # cli [subparser] -args
 | 
				
			||||||
    test_modes.add_argument('--barcodes', action='store_true',
 | 
					    command_subparsers = parser.add_subparsers(
 | 
				
			||||||
                            help="Test supported barcode types (Warning: Some printers must be "
 | 
					        title='ESCPOS Command',
 | 
				
			||||||
                                 "reset after attempting an unsupported barcode type.)")
 | 
					    )
 | 
				
			||||||
    test_modes.add_argument('--patterns', action='store_true',
 | 
					 | 
				
			||||||
                            help="Print test patterns")
 | 
					 | 
				
			||||||
    test_parser.set_defaults(func=test)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    args = parser.parse_args()
 | 
					    # Build the ESCPOS command arguments
 | 
				
			||||||
    args.func(args)
 | 
					    for command in ESCPOS_COMMANDS:
 | 
				
			||||||
 | 
					        parser_command = command_subparsers.add_parser(**command['parser'])
 | 
				
			||||||
 | 
					        parser_command.set_defaults(**command['defaults'])
 | 
				
			||||||
 | 
					        for argument in command['arguments']:
 | 
				
			||||||
 | 
					            option_strings = argument.pop('option_strings')
 | 
				
			||||||
 | 
					            parser_command.add_argument(*option_strings, **argument)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Build any custom arguments
 | 
				
			||||||
 | 
					    parser_command_demo = command_subparsers.add_parser('demo',
 | 
				
			||||||
 | 
					                                                        help='Demonstrates various functions')
 | 
				
			||||||
 | 
					    parser_command_demo.set_defaults(func='demo')
 | 
				
			||||||
 | 
					    demo_group = parser_command_demo.add_mutually_exclusive_group()
 | 
				
			||||||
 | 
					    demo_group.add_argument(
 | 
				
			||||||
 | 
					        '--barcodes-a',
 | 
				
			||||||
 | 
					        help='Print demo barcodes for function type A',
 | 
				
			||||||
 | 
					        action='store_true',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    demo_group.add_argument(
 | 
				
			||||||
 | 
					        '--barcodes-b',
 | 
				
			||||||
 | 
					        help='Print demo barcodes for function type B',
 | 
				
			||||||
 | 
					        action='store_true',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    demo_group.add_argument(
 | 
				
			||||||
 | 
					        '--qr',
 | 
				
			||||||
 | 
					        help='Print some demo QR codes',
 | 
				
			||||||
 | 
					        action='store_true',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    demo_group.add_argument(
 | 
				
			||||||
 | 
					        '--text',
 | 
				
			||||||
 | 
					        help='Print some demo text',
 | 
				
			||||||
 | 
					        action='store_true',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Get only arguments actually passed
 | 
				
			||||||
 | 
					    args_dict = vars(parser.parse_args())
 | 
				
			||||||
 | 
					    if not args_dict:
 | 
				
			||||||
 | 
					        parser.print_help()
 | 
				
			||||||
 | 
					        sys.exit()
 | 
				
			||||||
 | 
					    command_arguments = dict([k, v] for k, v in six.iteritems(args_dict) if v is not None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # If there was a config path passed, grab it
 | 
				
			||||||
 | 
					    config_path = command_arguments.pop('config', None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Load the configuration and defined printer
 | 
				
			||||||
 | 
					    saved_config = config.Config()
 | 
				
			||||||
 | 
					    saved_config.load(config_path)
 | 
				
			||||||
 | 
					    printer = saved_config.printer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not printer:
 | 
				
			||||||
 | 
					        raise Exception('No printers loaded from config')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    target_command = command_arguments.pop('func')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if hasattr(printer, target_command):
 | 
				
			||||||
 | 
					        # print command with args
 | 
				
			||||||
 | 
					        getattr(printer, target_command)(**command_arguments)
 | 
				
			||||||
 | 
					        if target_command in REQUIRES_NEWLINE:
 | 
				
			||||||
 | 
					            printer.text("\n")
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        command_arguments['printer'] = printer
 | 
				
			||||||
 | 
					        globals()[target_command](**command_arguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def demo(printer, **kwargs):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Prints specificed demos. Called when CLI is passed `demo`. This function
 | 
				
			||||||
 | 
					    uses the DEMO_FUNCTIONS dictionary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param printer: A printer from escpos.printer
 | 
				
			||||||
 | 
					    :param kwargs: A dict with a key for each function you want to test. It's
 | 
				
			||||||
 | 
					        in this format since it usually comes from argparse.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    for demo_choice in kwargs.keys():
 | 
				
			||||||
 | 
					        command = getattr(
 | 
				
			||||||
 | 
					            printer,
 | 
				
			||||||
 | 
					            demo_choice
 | 
				
			||||||
 | 
					            .replace('barcodes_a', 'barcode')
 | 
				
			||||||
 | 
					            .replace('barcodes_b', 'barcode')
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        for params in DEMO_FUNCTIONS[demo_choice]:
 | 
				
			||||||
 | 
					            command(**params)
 | 
				
			||||||
 | 
					        printer.cut()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    main()
 | 
					    main()
 | 
				
			||||||
 | 
					 | 
				
			||||||
# vim: set sw=4 sts=4 :
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										118
									
								
								escpos/config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								escpos/config.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					""" ESC/POS configuration manager.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This module contains the implentations of abstract base class :py:class:`Config`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from __future__ import absolute_import
 | 
				
			||||||
 | 
					from __future__ import division
 | 
				
			||||||
 | 
					from __future__ import print_function
 | 
				
			||||||
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import appdirs
 | 
				
			||||||
 | 
					import yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import printer
 | 
				
			||||||
 | 
					from . import exceptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Config(object):
 | 
				
			||||||
 | 
					    """  Configuration handler class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class loads configuration from a default or specificed directory. It
 | 
				
			||||||
 | 
					    can create your defined printer and return it to you.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    _app_name = 'python-escpos'
 | 
				
			||||||
 | 
					    _config_file = 'config.yaml'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        """ Initialize configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Remember to add anything that needs to be reset between configurations
 | 
				
			||||||
 | 
					        to self._reset_config
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self._has_loaded = False
 | 
				
			||||||
 | 
					        self._printer = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._printer_name = None
 | 
				
			||||||
 | 
					        self._printer_config = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _reset_config(self):
 | 
				
			||||||
 | 
					        """ Clear the loaded configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If we are loading a changed config, we don't want to have leftover
 | 
				
			||||||
 | 
					        data.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self._has_loaded = False
 | 
				
			||||||
 | 
					        self._printer = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._printer_name = None
 | 
				
			||||||
 | 
					        self._printer_config = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load(self, config_path=None):
 | 
				
			||||||
 | 
					        """ Load and parse the configuration file using pyyaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param config_path: An optional file path, file handle, or byte string
 | 
				
			||||||
 | 
					            for the configuration file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._reset_config()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not config_path:
 | 
				
			||||||
 | 
					            config_path = os.path.join(
 | 
				
			||||||
 | 
					                appdirs.user_config_dir(self._app_name),
 | 
				
			||||||
 | 
					                self._config_file
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            # First check if it's file like. If it is, pyyaml can load it.
 | 
				
			||||||
 | 
					            # I'm checking type instead of catching exceptions to keep the
 | 
				
			||||||
 | 
					            # exception handling simple
 | 
				
			||||||
 | 
					            if hasattr(config_path, 'read'):
 | 
				
			||||||
 | 
					                config = yaml.safe_load(config_path)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                # If it isn't, it's a path. We have to open it first, otherwise
 | 
				
			||||||
 | 
					                # pyyaml will try to read it as yaml
 | 
				
			||||||
 | 
					                with open(config_path, 'rb') as config_file:
 | 
				
			||||||
 | 
					                    config = yaml.safe_load(config_file)
 | 
				
			||||||
 | 
					        except EnvironmentError:
 | 
				
			||||||
 | 
					            raise exceptions.ConfigNotFoundError('Couldn\'t read config at {config_path}'.format(
 | 
				
			||||||
 | 
					                config_path=str(config_path),
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					        except yaml.YAMLError:
 | 
				
			||||||
 | 
					            raise exceptions.ConfigSyntaxError('Error parsing YAML')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'printer' in config:
 | 
				
			||||||
 | 
					            self._printer_config = config['printer']
 | 
				
			||||||
 | 
					            self._printer_name = self._printer_config.pop('type').title()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not self._printer_name or not hasattr(printer, self._printer_name):
 | 
				
			||||||
 | 
					                raise exceptions.ConfigSyntaxError(
 | 
				
			||||||
 | 
					                    'Printer type "{printer_name}" is invalid'.format(
 | 
				
			||||||
 | 
					                        printer_name=self._printer_name,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._has_loaded = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def printer(self):
 | 
				
			||||||
 | 
					        """ Returns a printer that was defined in the config, or throws an
 | 
				
			||||||
 | 
					        exception.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method loads the default config if one hasn't beeen already loaded.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if not self._has_loaded:
 | 
				
			||||||
 | 
					            self.load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self._printer_name:
 | 
				
			||||||
 | 
					            raise exceptions.ConfigSectionMissingError('printer')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self._printer:
 | 
				
			||||||
 | 
					            # We could catch init errors and make them a ConfigSyntaxError,
 | 
				
			||||||
 | 
					            # but I'll just let them pass
 | 
				
			||||||
 | 
					            self._printer = getattr(printer, self._printer_name)(**self._printer_config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self._printer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -442,7 +442,7 @@ class Escpos(object):
 | 
				
			|||||||
        self._raw(bc_types[bc.upper()])
 | 
					        self._raw(bc_types[bc.upper()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if function_type.upper() == "B":
 | 
					        if function_type.upper() == "B":
 | 
				
			||||||
            self._raw(chr(len(code)))
 | 
					            self._raw(six.int2byte(len(code)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Print Code
 | 
					        # Print Code
 | 
				
			||||||
        if code:
 | 
					        if code:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,9 @@ Result/Exit codes:
 | 
				
			|||||||
    - `80` = Invalid char code :py:exc:`~escpos.exceptions.CharCodeError`
 | 
					    - `80` = Invalid char code :py:exc:`~escpos.exceptions.CharCodeError`
 | 
				
			||||||
    - `90` = USB device not found :py:exc:`~escpos.exceptions.USBNotFoundError`
 | 
					    - `90` = USB device not found :py:exc:`~escpos.exceptions.USBNotFoundError`
 | 
				
			||||||
    - `100` = Set variable out of range :py:exc:`~escpos.exceptions.SetVariableError`
 | 
					    - `100` = Set variable out of range :py:exc:`~escpos.exceptions.SetVariableError`
 | 
				
			||||||
 | 
					    - `200` = Configuration not found :py:exc:`~escpos.exceptions.ConfigNotFoundError`
 | 
				
			||||||
 | 
					    - `210` = Configuration syntax error :py:exc:`~escpos.exceptions.ConfigSyntaxError`
 | 
				
			||||||
 | 
					    - `220` = Configuration section not found :py:exc:`~escpos.exceptions.ConfigSectionMissingError`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:author: `Manuel F Martinez <manpaz@bashlinux.com>`_ and others
 | 
					:author: `Manuel F Martinez <manpaz@bashlinux.com>`_ and others
 | 
				
			||||||
:organization: Bashlinux and `python-escpos <https://github.com/python-escpos>`_
 | 
					:organization: Bashlinux and `python-escpos <https://github.com/python-escpos>`_
 | 
				
			||||||
@@ -188,3 +191,48 @@ class SetVariableError(Error):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return "Set variable out of range"
 | 
					        return "Set variable out of range"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configuration errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConfigNotFoundError(Error):
 | 
				
			||||||
 | 
					    """ The configuration file was not found
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The default or passed configuration file could not be read
 | 
				
			||||||
 | 
					    Ths returncode for this exception is `200`.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, msg=""):
 | 
				
			||||||
 | 
					        Error.__init__(self, msg)
 | 
				
			||||||
 | 
					        self.msg = msg
 | 
				
			||||||
 | 
					        self.resultcode = 200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return "Configuration not found ({msg})".format(msg=self.msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConfigSyntaxError(Error):
 | 
				
			||||||
 | 
					    """ The configuration file is invalid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The syntax is incorrect
 | 
				
			||||||
 | 
					    Ths returncode for this exception is `210`.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, msg=""):
 | 
				
			||||||
 | 
					        Error.__init__(self, msg)
 | 
				
			||||||
 | 
					        self.msg = msg
 | 
				
			||||||
 | 
					        self.resultcode = 210
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return "Configuration syntax is invalid ({msg})".format(msg=self.msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConfigSectionMissingError(Error):
 | 
				
			||||||
 | 
					    """ The configuration file is missing a section
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The part of the config asked for doesn't exist in the loaded configuration
 | 
				
			||||||
 | 
					    Ths returncode for this exception is `220`.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, msg=""):
 | 
				
			||||||
 | 
					        Error.__init__(self, msg)
 | 
				
			||||||
 | 
					        self.msg = msg
 | 
				
			||||||
 | 
					        self.resultcode = 220
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return "Configuration section is missing ({msg})".format(msg=self.msg)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,6 +151,7 @@ class Serial(Escpos):
 | 
				
			|||||||
    def __del__(self):
 | 
					    def __del__(self):
 | 
				
			||||||
        """ Close Serial interface """
 | 
					        """ Close Serial interface """
 | 
				
			||||||
        if self.device is not None:
 | 
					        if self.device is not None:
 | 
				
			||||||
 | 
					            self.device.flush()
 | 
				
			||||||
            self.device.close()
 | 
					            self.device.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -209,6 +210,7 @@ class Network(Escpos):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __del__(self):
 | 
					    def __del__(self):
 | 
				
			||||||
        """ Close TCP connection """
 | 
					        """ Close TCP connection """
 | 
				
			||||||
 | 
					        self.device.shutdown(socket.SHUT_RDWR)
 | 
				
			||||||
        self.device.close()
 | 
					        self.device.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -256,4 +258,5 @@ class File(Escpos):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __del__(self):
 | 
					    def __del__(self):
 | 
				
			||||||
        """ Close system file """
 | 
					        """ Close system file """
 | 
				
			||||||
 | 
					        self.device.flush()
 | 
				
			||||||
        self.device.close()
 | 
					        self.device.close()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								setup.py
									
									
									
									
									
								
							@@ -72,10 +72,12 @@ setup(
 | 
				
			|||||||
        'qrcode>=4.0',
 | 
					        'qrcode>=4.0',
 | 
				
			||||||
        'pyserial',
 | 
					        'pyserial',
 | 
				
			||||||
        'six',
 | 
					        'six',
 | 
				
			||||||
 | 
					        'appdirs',
 | 
				
			||||||
 | 
					        'pyyaml',
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    setup_requires=[
 | 
					    setup_requires=[
 | 
				
			||||||
        'setuptools_scm',
 | 
					        'setuptools_scm',
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    tests_require=['tox', 'nose'],
 | 
					    tests_require=['tox', 'nose', 'scripttest'],
 | 
				
			||||||
    cmdclass={'test': Tox},
 | 
					    cmdclass={'test': Tox},
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								test/test_cli.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								test/test_cli.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					"""Test for the CLI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from __future__ import absolute_import
 | 
				
			||||||
 | 
					from __future__ import division
 | 
				
			||||||
 | 
					from __future__ import print_function
 | 
				
			||||||
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					from scripttest import TestFileEnvironment
 | 
				
			||||||
 | 
					from nose.tools import assert_equals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_DIR = os.path.abspath('test/test-cli-output')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEVFILE_NAME = 'testfile'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEVFILE = os.path.join(TEST_DIR, DEVFILE_NAME)
 | 
				
			||||||
 | 
					CONFIGFILE = 'testconfig.yaml'
 | 
				
			||||||
 | 
					CONFIG_YAML = '''
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					printer:
 | 
				
			||||||
 | 
					    type: file
 | 
				
			||||||
 | 
					    devfile: {testfile}
 | 
				
			||||||
 | 
					'''.format(
 | 
				
			||||||
 | 
					    testfile=DEVFILE,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestCLI:
 | 
				
			||||||
 | 
					    """ Contains setups, teardowns, and tests for CLI
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        """ Initalize the tests.
 | 
				
			||||||
 | 
					        Just define some vars here since most of them get set during
 | 
				
			||||||
 | 
					        setup_class and teardown_class
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.env = None
 | 
				
			||||||
 | 
					        self.default_args = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def setup_class():
 | 
				
			||||||
 | 
					        """ Create a config file to read from """
 | 
				
			||||||
 | 
					        with open(CONFIGFILE, 'w') as config:
 | 
				
			||||||
 | 
					            config.write(CONFIG_YAML)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def teardown_class():
 | 
				
			||||||
 | 
					        """ Remove config file """
 | 
				
			||||||
 | 
					        os.remove(CONFIGFILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setup(self):
 | 
				
			||||||
 | 
					        """ Create a file to print to and set up env"""
 | 
				
			||||||
 | 
					        self.env = TestFileEnvironment(
 | 
				
			||||||
 | 
					            base_path=TEST_DIR,
 | 
				
			||||||
 | 
					            cwd=os.getcwd(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.default_args = (
 | 
				
			||||||
 | 
					            sys.executable,
 | 
				
			||||||
 | 
					            '-mescpos.cli',
 | 
				
			||||||
 | 
					            '-c',
 | 
				
			||||||
 | 
					            CONFIGFILE,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fhandle = open(DEVFILE, 'a')
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            os.utime(DEVFILE, None)
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            fhandle.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def teardown(self):
 | 
				
			||||||
 | 
					        """ Destroy printer file and env """
 | 
				
			||||||
 | 
					        os.remove(DEVFILE)
 | 
				
			||||||
 | 
					        self.env.clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_cli_help(self):
 | 
				
			||||||
 | 
					        """ Test getting help from cli """
 | 
				
			||||||
 | 
					        result = self.env.run(sys.executable, '-mescpos.cli', '-h')
 | 
				
			||||||
 | 
					        assert not result.stderr
 | 
				
			||||||
 | 
					        assert 'usage' in result.stdout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_cli_text(self):
 | 
				
			||||||
 | 
					        """ Make sure text returns what we sent it """
 | 
				
			||||||
 | 
					        test_text = 'this is some text'
 | 
				
			||||||
 | 
					        result = self.env.run(
 | 
				
			||||||
 | 
					            *(self.default_args + (
 | 
				
			||||||
 | 
					                'text',
 | 
				
			||||||
 | 
					                '--txt',
 | 
				
			||||||
 | 
					                test_text,
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        assert not result.stderr
 | 
				
			||||||
 | 
					        assert DEVFILE_NAME in result.files_updated.keys()
 | 
				
			||||||
 | 
					        assert_equals(
 | 
				
			||||||
 | 
					            result.files_updated[DEVFILE_NAME].bytes,
 | 
				
			||||||
 | 
					            test_text + '\n'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_cli_text_inavlid_args(self):
 | 
				
			||||||
 | 
					        """ Test a failure to send valid arguments """
 | 
				
			||||||
 | 
					        result = self.env.run(
 | 
				
			||||||
 | 
					            *(self.default_args + (
 | 
				
			||||||
 | 
					                'text',
 | 
				
			||||||
 | 
					                '--invalid-param',
 | 
				
			||||||
 | 
					                'some data'
 | 
				
			||||||
 | 
					            )),
 | 
				
			||||||
 | 
					            expect_error=True,
 | 
				
			||||||
 | 
					            expect_stderr=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        assert_equals(result.returncode, 2)
 | 
				
			||||||
 | 
					        assert 'error:' in result.stderr
 | 
				
			||||||
 | 
					        assert not result.files_updated
 | 
				
			||||||
		Reference in New Issue
	
	Block a user