Consolidate glance.utils into glance.common.utils
Change-Id: I2512b1d3d47f7d3f45707aa5774d9b4b3cb5c26e
This commit is contained in:
parent
45c7280783
commit
bd00433233
@ -43,8 +43,8 @@ gettext.install('glance', unicode=1)
|
||||
from glance import client as glance_client
|
||||
from glance import version
|
||||
from glance.common import exception
|
||||
from glance.common import utils as common_utils
|
||||
from glance import utils
|
||||
from glance.common import utils
|
||||
|
||||
|
||||
SUCCESS = 0
|
||||
FAILURE = 1
|
||||
@ -214,7 +214,7 @@ EXAMPLES
|
||||
return FAILURE
|
||||
|
||||
image_meta = {'name': fields.pop('name'),
|
||||
'is_public': common_utils.bool_from_string(
|
||||
'is_public': utils.bool_from_string(
|
||||
fields.pop('is_public', False)),
|
||||
'disk_format': fields.pop('disk_format', 'raw'),
|
||||
'min_disk': fields.pop('min_disk', 0),
|
||||
@ -340,7 +340,7 @@ to spell field names correctly. :)"""
|
||||
|
||||
# Have to handle "boolean" values specially...
|
||||
if 'is_public' in fields:
|
||||
image_meta['is_public'] = common_utils.bool_from_string(
|
||||
image_meta['is_public'] = utils.bool_from_string(
|
||||
fields.pop('is_public'))
|
||||
|
||||
# Add custom attributes, which are all the arguments remaining
|
||||
|
@ -47,7 +47,6 @@ from glance.store import (get_from_backend,
|
||||
get_store_from_scheme,
|
||||
UnsupportedBackend)
|
||||
from glance import registry
|
||||
from glance import utils
|
||||
|
||||
|
||||
logger = logging.getLogger('glance.api.v1.images')
|
||||
@ -596,7 +595,7 @@ class ImageDeserializer(wsgi.JSONRequestDeserializer):
|
||||
|
||||
def _deserialize(self, request):
|
||||
result = {}
|
||||
result['image_meta'] = utils.get_image_meta_from_headers(request)
|
||||
result['image_meta'] = wsgi.get_image_meta_from_headers(request)
|
||||
data = request.body_file if self.has_body(request) else None
|
||||
result['image_data'] = data
|
||||
return result
|
||||
@ -630,7 +629,7 @@ class ImageSerializer(wsgi.JSONResponseSerializer):
|
||||
:param response: The Webob Response object
|
||||
:param image_meta: Mapping of image metadata
|
||||
"""
|
||||
headers = utils.image_meta_to_http_headers(image_meta)
|
||||
headers = wsgi.image_meta_to_http_headers(image_meta)
|
||||
|
||||
for k, v in headers.items():
|
||||
response.headers[k] = v
|
||||
|
@ -26,7 +26,7 @@ import os
|
||||
from glance.api.v1 import images as v1_images
|
||||
from glance.common import client as base_client
|
||||
from glance.common import exception
|
||||
from glance import utils
|
||||
from glance.common import wsgi
|
||||
|
||||
#TODO(jaypipes) Allow a logger param for client classes
|
||||
|
||||
@ -82,7 +82,7 @@ class V1Client(base_client.BaseClient):
|
||||
"""
|
||||
res = self.do_request("GET", "/images/%s" % image_id)
|
||||
|
||||
image = utils.get_image_meta_from_headers(res)
|
||||
image = wsgi.get_image_meta_from_headers(res)
|
||||
return image, base_client.ImageBodyIterator(res)
|
||||
|
||||
def get_image_meta(self, image_id):
|
||||
@ -93,7 +93,7 @@ class V1Client(base_client.BaseClient):
|
||||
"""
|
||||
res = self.do_request("HEAD", "/images/%s" % image_id)
|
||||
|
||||
image = utils.get_image_meta_from_headers(res)
|
||||
image = wsgi.get_image_meta_from_headers(res)
|
||||
return image
|
||||
|
||||
def _get_image_size(self, image_data):
|
||||
@ -136,7 +136,7 @@ class V1Client(base_client.BaseClient):
|
||||
|
||||
:retval The newly-stored image's metadata.
|
||||
"""
|
||||
headers = utils.image_meta_to_http_headers(image_meta or {})
|
||||
headers = wsgi.image_meta_to_http_headers(image_meta or {})
|
||||
|
||||
if image_data:
|
||||
body = image_data
|
||||
@ -159,7 +159,7 @@ class V1Client(base_client.BaseClient):
|
||||
if image_meta is None:
|
||||
image_meta = {}
|
||||
|
||||
headers = utils.image_meta_to_http_headers(image_meta)
|
||||
headers = wsgi.image_meta_to_http_headers(image_meta)
|
||||
|
||||
if image_data:
|
||||
body = image_data
|
||||
@ -275,7 +275,7 @@ class V1Client(base_client.BaseClient):
|
||||
Pre-fetch a specified image from the cache
|
||||
"""
|
||||
res = self.do_request("HEAD", "/images/%s" % image_id)
|
||||
image = utils.get_image_meta_from_headers(res)
|
||||
image = wsgi.get_image_meta_from_headers(res)
|
||||
self.do_request("PUT", "/cached_images/%s" % image_id)
|
||||
return True
|
||||
|
||||
|
@ -33,6 +33,9 @@ import uuid
|
||||
|
||||
from glance.common import exception
|
||||
|
||||
|
||||
logger = logging.getLogger('glance.utils')
|
||||
|
||||
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
||||
|
||||
|
||||
@ -128,3 +131,65 @@ def safe_remove(path):
|
||||
except OSError, e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
|
||||
class PrettyTable(object):
|
||||
"""Creates an ASCII art table for use in bin/glance
|
||||
|
||||
Example:
|
||||
|
||||
ID Name Size Hits
|
||||
--- ----------------- ------------ -----
|
||||
122 image 22 0
|
||||
"""
|
||||
def __init__(self):
|
||||
self.columns = []
|
||||
|
||||
def add_column(self, width, label="", just='l'):
|
||||
"""Add a column to the table
|
||||
|
||||
:param width: number of characters wide the column should be
|
||||
:param label: column heading
|
||||
:param just: justification for the column, 'l' for left,
|
||||
'r' for right
|
||||
"""
|
||||
self.columns.append((width, label, just))
|
||||
|
||||
def make_header(self):
|
||||
label_parts = []
|
||||
break_parts = []
|
||||
for width, label, _ in self.columns:
|
||||
# NOTE(sirp): headers are always left justified
|
||||
label_part = self._clip_and_justify(label, width, 'l')
|
||||
label_parts.append(label_part)
|
||||
|
||||
break_part = '-' * width
|
||||
break_parts.append(break_part)
|
||||
|
||||
label_line = ' '.join(label_parts)
|
||||
break_line = ' '.join(break_parts)
|
||||
return '\n'.join([label_line, break_line])
|
||||
|
||||
def make_row(self, *args):
|
||||
row = args
|
||||
row_parts = []
|
||||
for data, (width, _, just) in zip(row, self.columns):
|
||||
row_part = self._clip_and_justify(data, width, just)
|
||||
row_parts.append(row_part)
|
||||
|
||||
row_line = ' '.join(row_parts)
|
||||
return row_line
|
||||
|
||||
@staticmethod
|
||||
def _clip_and_justify(data, width, just):
|
||||
# clip field to column width
|
||||
clipped_data = str(data)[:width]
|
||||
|
||||
if just == 'r':
|
||||
# right justify
|
||||
justified = clipped_data.rjust(width)
|
||||
else:
|
||||
# left justify
|
||||
justified = clipped_data.ljust(width)
|
||||
|
||||
return justified
|
||||
|
@ -405,3 +405,73 @@ class Resource(object):
|
||||
pass
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def image_meta_to_http_headers(image_meta):
|
||||
"""
|
||||
Returns a set of image metadata into a dict
|
||||
of HTTP headers that can be fed to either a Webob
|
||||
Request object or an httplib.HTTP(S)Connection object
|
||||
|
||||
:param image_meta: Mapping of image metadata
|
||||
"""
|
||||
headers = {}
|
||||
for k, v in image_meta.items():
|
||||
if v is None:
|
||||
v = ''
|
||||
if k == 'properties':
|
||||
for pk, pv in v.items():
|
||||
if pv is None:
|
||||
pv = ''
|
||||
headers["x-image-meta-property-%s"
|
||||
% pk.lower()] = unicode(pv)
|
||||
else:
|
||||
headers["x-image-meta-%s" % k.lower()] = unicode(v)
|
||||
return headers
|
||||
|
||||
|
||||
def get_image_meta_from_headers(response):
|
||||
"""
|
||||
Processes HTTP headers from a supplied response that
|
||||
match the x-image-meta and x-image-meta-property and
|
||||
returns a mapping of image metadata and properties
|
||||
|
||||
:param response: Response to process
|
||||
"""
|
||||
result = {}
|
||||
properties = {}
|
||||
|
||||
if hasattr(response, 'getheaders'): # httplib.HTTPResponse
|
||||
headers = response.getheaders()
|
||||
else: # webob.Response
|
||||
headers = response.headers.items()
|
||||
|
||||
for key, value in headers:
|
||||
key = str(key.lower())
|
||||
if key.startswith('x-image-meta-property-'):
|
||||
field_name = key[len('x-image-meta-property-'):].replace('-', '_')
|
||||
properties[field_name] = value or None
|
||||
elif key.startswith('x-image-meta-'):
|
||||
field_name = key[len('x-image-meta-'):].replace('-', '_')
|
||||
result[field_name] = value or None
|
||||
result['properties'] = properties
|
||||
if 'size' in result:
|
||||
result['size'] = int(result['size'])
|
||||
if 'is_public' in result:
|
||||
result['is_public'] = bool_from_header_value(result['is_public'])
|
||||
if 'deleted' in result:
|
||||
result['deleted'] = bool_from_header_value(result['deleted'])
|
||||
return result
|
||||
|
||||
|
||||
def bool_from_header_value(value):
|
||||
"""
|
||||
Returns True if value is a boolean True or the
|
||||
string 'true', case-insensitive, False otherwise
|
||||
"""
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
elif isinstance(value, (basestring, unicode)):
|
||||
if str(value).lower() == 'true':
|
||||
return True
|
||||
return False
|
||||
|
@ -17,100 +17,15 @@
|
||||
|
||||
import unittest
|
||||
|
||||
from glance import utils
|
||||
from glance.common import utils as common_utils
|
||||
from glance.common import utils
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
|
||||
"""Test routines in glance.utils"""
|
||||
|
||||
def test_headers_are_unicode(self):
|
||||
"""
|
||||
Verifies that the headers returned by conversion code are unicode.
|
||||
|
||||
Headers are passed via http in non-testing mode, which automatically
|
||||
converts them to unicode. Verifying that the method does the
|
||||
conversion proves that we aren't passing data that works in tests
|
||||
but will fail in production.
|
||||
"""
|
||||
fixture = {'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'type': 'kernel',
|
||||
'size': 19,
|
||||
'location': "file:///tmp/glance-tests/2",
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}}
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
for k, v in headers.iteritems():
|
||||
self.assert_(isinstance(v, unicode), "%s is not unicode" % v)
|
||||
|
||||
def test_data_passed_properly_through_headers(self):
|
||||
"""
|
||||
Verifies that data is the same after being passed through headers
|
||||
"""
|
||||
fixture = {'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'deleted': False,
|
||||
'type': 'kernel',
|
||||
'name': None,
|
||||
'size': 19,
|
||||
'location': "file:///tmp/glance-tests/2",
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}}
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
|
||||
class FakeResponse():
|
||||
pass
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = utils.get_image_meta_from_headers(response)
|
||||
for k, v in fixture.iteritems():
|
||||
self.assertEqual(v, result[k])
|
||||
|
||||
def test_boolean_header_values(self):
|
||||
"""
|
||||
Tests that boolean headers like is_public can be set
|
||||
to True if any of ('True', 'On', 1) is provided, case-insensitive
|
||||
"""
|
||||
fixtures = [{'is_public': True},
|
||||
{'is_public': 'True'},
|
||||
{'is_public': 'true'}]
|
||||
|
||||
expected = {'is_public': True}
|
||||
|
||||
class FakeResponse():
|
||||
pass
|
||||
|
||||
for fixture in fixtures:
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = utils.get_image_meta_from_headers(response)
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, result[k])
|
||||
|
||||
# Ensure False for other values...
|
||||
fixtures = [{'is_public': False},
|
||||
{'is_public': 'Off'},
|
||||
{'is_public': 'on'},
|
||||
{'is_public': '1'},
|
||||
{'is_public': 'False'}]
|
||||
|
||||
expected = {'is_public': False}
|
||||
|
||||
for fixture in fixtures:
|
||||
headers = utils.image_meta_to_http_headers(fixture)
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = utils.get_image_meta_from_headers(response)
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, result[k])
|
||||
|
||||
def test_generate_uuid_format(self):
|
||||
"""Check the format of a uuid"""
|
||||
uuid = common_utils.generate_uuid()
|
||||
uuid = utils.generate_uuid()
|
||||
self.assertTrue(isinstance(uuid, basestring))
|
||||
self.assertTrue(len(uuid), 36)
|
||||
# make sure there are 4 dashes
|
||||
@ -118,15 +33,15 @@ class TestUtils(unittest.TestCase):
|
||||
|
||||
def test_generate_uuid_unique(self):
|
||||
"""Ensure generate_uuid will return unique values"""
|
||||
uuids = [common_utils.generate_uuid() for i in range(5)]
|
||||
uuids = [utils.generate_uuid() for i in range(5)]
|
||||
# casting to set will drop duplicate values
|
||||
unique = set(uuids)
|
||||
self.assertEqual(len(uuids), len(list(unique)))
|
||||
|
||||
def test_is_uuid_like_success(self):
|
||||
fixture = 'b694bf02-6b01-4905-a50e-fcf7bce7e4d2'
|
||||
self.assertTrue(common_utils.is_uuid_like(fixture))
|
||||
self.assertTrue(utils.is_uuid_like(fixture))
|
||||
|
||||
def test_is_uuid_like_fails(self):
|
||||
fixture = 'pants'
|
||||
self.assertFalse(common_utils.is_uuid_like(fixture))
|
||||
self.assertFalse(utils.is_uuid_like(fixture))
|
||||
|
@ -182,3 +182,89 @@ class JSONRequestDeserializerTest(unittest.TestCase):
|
||||
actual = wsgi.JSONRequestDeserializer().default(request)
|
||||
expected = {"body": {"key": "value"}}
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
||||
class TestHelpers(unittest.TestCase):
|
||||
|
||||
def test_headers_are_unicode(self):
|
||||
"""
|
||||
Verifies that the headers returned by conversion code are unicode.
|
||||
|
||||
Headers are passed via http in non-testing mode, which automatically
|
||||
converts them to unicode. Verifying that the method does the
|
||||
conversion proves that we aren't passing data that works in tests
|
||||
but will fail in production.
|
||||
"""
|
||||
fixture = {'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'type': 'kernel',
|
||||
'size': 19,
|
||||
'location': "file:///tmp/glance-tests/2",
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}}
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
for k, v in headers.iteritems():
|
||||
self.assert_(isinstance(v, unicode), "%s is not unicode" % v)
|
||||
|
||||
def test_data_passed_properly_through_headers(self):
|
||||
"""
|
||||
Verifies that data is the same after being passed through headers
|
||||
"""
|
||||
fixture = {'name': 'fake public image',
|
||||
'is_public': True,
|
||||
'deleted': False,
|
||||
'type': 'kernel',
|
||||
'name': None,
|
||||
'size': 19,
|
||||
'location': "file:///tmp/glance-tests/2",
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS'}}
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
|
||||
class FakeResponse():
|
||||
pass
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = wsgi.get_image_meta_from_headers(response)
|
||||
for k, v in fixture.iteritems():
|
||||
self.assertEqual(v, result[k])
|
||||
|
||||
def test_boolean_header_values(self):
|
||||
"""
|
||||
Tests that boolean headers like is_public can be set
|
||||
to True if any of ('True', 'On', 1) is provided, case-insensitive
|
||||
"""
|
||||
fixtures = [{'is_public': True},
|
||||
{'is_public': 'True'},
|
||||
{'is_public': 'true'}]
|
||||
|
||||
expected = {'is_public': True}
|
||||
|
||||
class FakeResponse():
|
||||
pass
|
||||
|
||||
for fixture in fixtures:
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = wsgi.get_image_meta_from_headers(response)
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, result[k])
|
||||
|
||||
# Ensure False for other values...
|
||||
fixtures = [{'is_public': False},
|
||||
{'is_public': 'Off'},
|
||||
{'is_public': 'on'},
|
||||
{'is_public': '1'},
|
||||
{'is_public': 'False'}]
|
||||
|
||||
expected = {'is_public': False}
|
||||
|
||||
for fixture in fixtures:
|
||||
headers = wsgi.image_meta_to_http_headers(fixture)
|
||||
|
||||
response = FakeResponse()
|
||||
response.headers = headers
|
||||
result = wsgi.get_image_meta_from_headers(response)
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, result[k])
|
||||
|
165
glance/utils.py
165
glance/utils.py
@ -1,165 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
A few utility routines used throughout Glance
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('glance.utils')
|
||||
|
||||
|
||||
def image_meta_to_http_headers(image_meta):
|
||||
"""
|
||||
Returns a set of image metadata into a dict
|
||||
of HTTP headers that can be fed to either a Webob
|
||||
Request object or an httplib.HTTP(S)Connection object
|
||||
|
||||
:param image_meta: Mapping of image metadata
|
||||
"""
|
||||
headers = {}
|
||||
for k, v in image_meta.items():
|
||||
if v is None:
|
||||
v = ''
|
||||
if k == 'properties':
|
||||
for pk, pv in v.items():
|
||||
if pv is None:
|
||||
pv = ''
|
||||
headers["x-image-meta-property-%s"
|
||||
% pk.lower()] = unicode(pv)
|
||||
else:
|
||||
headers["x-image-meta-%s" % k.lower()] = unicode(v)
|
||||
return headers
|
||||
|
||||
|
||||
def get_image_meta_from_headers(response):
|
||||
"""
|
||||
Processes HTTP headers from a supplied response that
|
||||
match the x-image-meta and x-image-meta-property and
|
||||
returns a mapping of image metadata and properties
|
||||
|
||||
:param response: Response to process
|
||||
"""
|
||||
result = {}
|
||||
properties = {}
|
||||
|
||||
if hasattr(response, 'getheaders'): # httplib.HTTPResponse
|
||||
headers = response.getheaders()
|
||||
else: # webob.Response
|
||||
headers = response.headers.items()
|
||||
|
||||
for key, value in headers:
|
||||
key = str(key.lower())
|
||||
if key.startswith('x-image-meta-property-'):
|
||||
field_name = key[len('x-image-meta-property-'):].replace('-', '_')
|
||||
properties[field_name] = value or None
|
||||
elif key.startswith('x-image-meta-'):
|
||||
field_name = key[len('x-image-meta-'):].replace('-', '_')
|
||||
result[field_name] = value or None
|
||||
result['properties'] = properties
|
||||
if 'size' in result:
|
||||
result['size'] = int(result['size'])
|
||||
if 'is_public' in result:
|
||||
result['is_public'] = bool_from_header_value(result['is_public'])
|
||||
if 'deleted' in result:
|
||||
result['deleted'] = bool_from_header_value(result['deleted'])
|
||||
return result
|
||||
|
||||
|
||||
def bool_from_header_value(value):
|
||||
"""
|
||||
Returns True if value is a boolean True or the
|
||||
string 'true', case-insensitive, False otherwise
|
||||
"""
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
elif isinstance(value, (basestring, unicode)):
|
||||
if str(value).lower() == 'true':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def has_body(req):
|
||||
"""
|
||||
Returns whether a Webob.Request object will possess an entity body.
|
||||
|
||||
:param req: Webob.Request object
|
||||
"""
|
||||
return req.content_length or 'transfer-encoding' in req.headers
|
||||
|
||||
|
||||
class PrettyTable(object):
|
||||
"""Creates an ASCII art table for use in bin/glance
|
||||
|
||||
Example:
|
||||
|
||||
ID Name Size Hits
|
||||
--- ----------------- ------------ -----
|
||||
122 image 22 0
|
||||
"""
|
||||
def __init__(self):
|
||||
self.columns = []
|
||||
|
||||
def add_column(self, width, label="", just='l'):
|
||||
"""Add a column to the table
|
||||
|
||||
:param width: number of characters wide the column should be
|
||||
:param label: column heading
|
||||
:param just: justification for the column, 'l' for left,
|
||||
'r' for right
|
||||
"""
|
||||
self.columns.append((width, label, just))
|
||||
|
||||
def make_header(self):
|
||||
label_parts = []
|
||||
break_parts = []
|
||||
for width, label, _ in self.columns:
|
||||
# NOTE(sirp): headers are always left justified
|
||||
label_part = self._clip_and_justify(label, width, 'l')
|
||||
label_parts.append(label_part)
|
||||
|
||||
break_part = '-' * width
|
||||
break_parts.append(break_part)
|
||||
|
||||
label_line = ' '.join(label_parts)
|
||||
break_line = ' '.join(break_parts)
|
||||
return '\n'.join([label_line, break_line])
|
||||
|
||||
def make_row(self, *args):
|
||||
row = args
|
||||
row_parts = []
|
||||
for data, (width, _, just) in zip(row, self.columns):
|
||||
row_part = self._clip_and_justify(data, width, just)
|
||||
row_parts.append(row_part)
|
||||
|
||||
row_line = ' '.join(row_parts)
|
||||
return row_line
|
||||
|
||||
@staticmethod
|
||||
def _clip_and_justify(data, width, just):
|
||||
# clip field to column width
|
||||
clipped_data = str(data)[:width]
|
||||
|
||||
if just == 'r':
|
||||
# right justify
|
||||
justified = clipped_data.rjust(width)
|
||||
else:
|
||||
# left justify
|
||||
justified = clipped_data.ljust(width)
|
||||
|
||||
return justified
|
Loading…
Reference in New Issue
Block a user