Source code for gapic.utils.lines

# Copyright 2018 Google LLC
#
# 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
#
#     https://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.

import textwrap
from typing import Iterable


[docs]def sort_lines(text: str, dedupe: bool = True) -> str: """Sort the individual lines of a block of text. Args: dedupe (bool): Remove duplicate lines with the same text. Useful for dealing with import statements in templates. """ # Preserve leading or trailing newlines. leading = '\n' if text.startswith('\n') else '' trailing = '\n' if text.endswith('\n') else '' # Split the text into individual lines, throwing away any empty lines. lines: Iterable[str] = (i for i in text.strip().split('\n') if i.strip()) # De-duplicate the lines if requested. if dedupe: lines = set(lines) # Return the final string. answer = '\n'.join(sorted(lines)) return f'{leading}{answer}{trailing}'
[docs]def wrap(text: str, width: int, *, offset: int = None, indent: int = 0) -> str: """Wrap the given string to the given width. This uses :meth:`textwrap.fill` under the hood, but provides useful offset functionality for Jinja templates. This is provided to all templates as the ``wrap`` filter. Args: text (str): The initial text string. width (int): The width at which to wrap the text. If offset is provided, these are automatically counted against this. offset (int): The offset for the first line of text. This value is subtracted from ``width`` for the first line only, and is intended to represent the vertical position of the first line as already present in the template. Defaults to the value of ``indent``. indent (int): The number of spaces to indent all lines after the first one. Returns: str: The wrapped string. """ # Sanity check: If there is empty text, abort. if not text: return '' # If the offset is None, default it to the indent value. if offset is None: offset = indent # Protocol buffers preserves single initial spaces after line breaks # when parsing comments (such as the space before the "w" in "when" here). # Re-wrapping causes these to be two spaces; correct for this. text = text.replace('\n ', '\n') # Break off the first line of the string to address non-zero offsets. first = text.split('\n')[0] + '\n' if len(first) > width - offset: initial = textwrap.wrap(first, break_long_words=False, width=width - offset, ) # Strip the first \n from the text so it is not misidentified as an # intentionally short line below. text = text.replace('\n', ' ', 1) # Save the new `first` line. first = f'{initial[0]}\n' text = text[len(first):].strip() if not text: return first.strip() # Tokenize the rest of the text to try to preserve line breaks # that semantically matter. tokens = [] token = '' for line in text.split('\n'): token += line + '\n' if len(line) < width * 0.75: tokens.append(token) token = '' if token: tokens.append(token) # Wrap the remainder of the string at the desired width. return '{first}{text}'.format( first=first, text='\n'.join([textwrap.fill( break_long_words=False, initial_indent=' ' * indent, subsequent_indent=' ' * indent, text=token, width=width, ) for token in tokens]), ).rstrip('\n')