0

I'm trying to edit a setup.py file with sed and this is the pattern I need to match:

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import fnmatch
import os
import re
import sys

from setuptools import Command
from setuptools import find_packages
from setuptools import setup
from setuptools.command.install import install as InstallCommandBase
from setuptools.dist import Distribution

DOCLINES = __doc__.split('\n')

_VERSION = '1.0.0'

REQUIRED_PACKAGES = [
    'absl-py >= 0.7.0',
    'astunparse == 1.6.3',
    'backports.weakref >= 1.0rc1;python_version<"3.4"',
    'scipy == 1.2.2;python_version<"3"',
]

After matching REQUIRED_PACKAGES = [ .* ] I want to insert a line 'test == 1.1.0', before the closing ].

So final will look like this:

REQUIRED_PACKAGES = [
    'absl-py >= 0.7.0',
    'astunparse == 1.6.3',
    'backports.weakref >= 1.0rc1;python_version<"3.4"',
    'scipy == 1.2.2;python_version<"3"',    
    'test == 1.1.0',
]

I tried with Python, but looking for cleaner and simpler options.

import re


SEARCH_DICT = {
    'required_packages': re.compile(
        r'REQUIRED_PACKAGES = (?P<required_packages>.*)\n')
}

TEST_LIBRARY = '\t\t\'test==1.0.0\'\n'


def _parse_line(line):
    """
    Do a regex search against all defined regexes and
    return the key and match result of the first matching regex

    """

    for key, rx in SEARCH_DICT.items():
        match = rx.search(line)
        if match:
            return key, match
    # if there are no matches
    return None, None


def parse_file(filepath):
    """
    Parse text at given filepath

    Parameters
    ----------
    filepath : str
        Filepath for file_object to be parsed

    Returns
    -------
    data : file contents
    """

    data = []  # create an empty list to collect the data
    line_index = -1 
    # open the file and read through it line by line

    with open(filepath, 'r+') as file_object:
        line = file_object.readline()
        line_index+=1
        while line:
            # at each line check for a match with a regex
            key, match = _parse_line(line)
            if key == 'required_packages':
                required_packages_start = match.group('required_packages')
                if required_packages_start == '[':                    
                    print('Found REQUIRED_PACKAGES')
                    while line.strip():                        
                        library = line.rstrip()
                        if library == ']': # End of required packages
                            return line_index
                        line = file_object.readline()
                        line_index+=1                     

            line = file_object.readline()  
            line_index+=1          
        file_object.readline()
        line_index+=1

    return line_index


line_index = parse_file('test.test')
lines = None
if line_index:
    with open('test.test', 'r') as file_handler:
        lines = file_handler.readlines()
    lines.insert(line_index, TEST_LIBRARY)
    with open('test.out', 'w') as file_handler:
        file_handler.writelines(lines)
1
  • 1
    I made a mistake, jus edited, thanks!
    – gogasca
    Feb 26 '20 at 18:21
2

sed can insert a line:

sed "/REQUIRED_PACKAGES = \[/, /]/ {/]/i\    'test == 1.1.0',
;}" FILE
2
  • Thanks, I get sed: 1: "/REQUIRED_PACKAGES = \[ ...": extra characters after \ at the end of i command
    – gogasca
    Feb 26 '20 at 19:39
  • 1
    Sounds like a problem with copy/pasting into a shell. Try entering the first line then press <enter> and enter the second line
    – guest
    Feb 26 '20 at 22:39
1
$ cat file
_VERSION = '1.0.0'

REQUIRED_PACKAGES = [
    'absl-py >= 0.7.0',
    'astunparse == 1.6.3',
    'backports.weakref >= 1.0rc1;python_version<"3.4"',
    'scipy == 1.2.2;python_version<"3"',
]

.

$ cat tst.awk
/REQUIRED_PACKAGES = \[/ { inBlock=1 }
inBlock {
    if ( /]/ ) {
        sub(/[^[:space:]].*/,"",prev)
        print prev "\047test == 1.1.0\047,"
        inBlock = prev = ""
    }
    prev = $0
}
{ print }

.

$ awk -f tst.awk file
_VERSION = '1.0.0'

REQUIRED_PACKAGES = [
    'absl-py >= 0.7.0',
    'astunparse == 1.6.3',
    'backports.weakref >= 1.0rc1;python_version<"3.4"',
    'scipy == 1.2.2;python_version<"3"',
    'test == 1.1.0',
]

Note that the above inserts the new text using whatever indentation the previous line was using.

1

sed is a line-oriented tool. I believe better would be to use awk, it will more easily detect that block to which you want and append a line. There may be a more elegant way, but this does what you want:

awk '$0=="REQUIRED_PACKAGES = [" {found=1}
    found==1 && $0=="]" {print "    \047test == 1.1.0\047"; found=0}
    {print}' setup.py

Quite simple to understand: It prints every line of the file, but before printing the closing bracket of the REQUIRED_PACKAGES block it prints the line you want to append.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.