Author Archives: Christian Hopps

Extracting an Environment Variable From a Process

Occasionally I find it necessary to extract the value of an environment variable of a running process. For example to examine the TMUX value of a given process. I’ll use this later to determine if I should signal an emacs process when a TMUX session is reattached. This function depends on /proc/<pid>/environ being supported (apparently a linux thing). Here’s the function.

[Ed: updated version with suggested replacement from KnowsBash reddit-bash comment]

get-pid-env-var () {
    if [ -z "$1" -o -z "$2" ]; then
        echo "usage: get-pid-env-var pid var"
        return 1
    fi
    local key value
    while IFS='=' read -rd '' key value; do
        if [[ $key = "$2" ]]; then
            printf '%s\n' "$value"
            break
        fi
    done < "/proc/$1/environ"
}

And here it is in use.

[22:36:28 ~]$ ps x | grep emacs
24414 pts/1    S+     0:04 emacs -nw .bashrc
26751 pts/4    S+     0:00 grep emacs
[22:36:32 ~]$ get-pid-env-var 24414 TMUX
/tmp/tmux-21995/default,24155,0

Here’s the old version.

get-pid-env-var () {
    if [ -z "$1" -o -z "$2" ]; then
        echo "usage: get-pid-env-var pid var"
        return 1
    fi
    local VAR=$(tr "\0" "\n" < /proc/$1/environ | grep "$2=")
    if [ -z "$VAR" ]; then
        return 1
    fi
    echo ${VAR#$2=}
    return 0
}

A Pymacs Code Example

This is a simple example of using pymacs to fix a flake8 warning. The warning is covering use of = or ! with None. The fixer is invoked with the point on the line containing the warning or error. This is part of a system I’m building to support auto-correction of flake8 and pylint warnings in emacs. As the system matures I plan to host it on github so that others may also contribute to it.

Here is the actual fixer function with a utility function to get the string of the current line. A key to understanding what’s going on with the code is that lisp.function(arg) is the equivalent of (function arg) in elisp.

def get_line_string ():
    """Get the start, end and string of the line the point is on."""
    start = lisp.line_beginning_position()
    end = lisp.line_end_position()
    return start, end, lisp.buffer_substring(start, end)

def fixer_equals_none (unused_error, unused_errtext):
    start, end, line = get_line_string()
    newline = re.sub(r"==\s*None", r"is None", line)
    newline = re.sub(r"!=\s*None", r"is not None", newline)
    if newline != line:
        lisp.delete_region(start, end)
        lisp.insert(newline)

Obviously this function is invoked from somewhere else with the error number and text (e.g., E711 and E711: error text). The following is the code that does that.

fixers = {
    # ...
    "E711": fixer_equals_none,
}

def flymake_fix_current_line ():
    lineno = lisp.flymake_current_line_no()
    errinfo = lisp.flymake_err_info
    errlist = lisp.nth(0, lisp.flymake_find_err_info(errinfo.value(), lineno))
    menudata = lisp.flymake_make_err_menu_data(lineno, errlist)
    did_something = False
    #             caadr (x ((e1 e2 ...))) -> (e1 e2 ...)
    for errtxt in menudata[1][0]:
        if not errtxt:
            break
        try:
            m = re.match(r"([EFW]\d+) .*", errtxt)
        except Exception as ex:
            continue
        if not m:
            print("nomatch on {}".format(errtxt))
            continue
        key = m.group(1)
        if key in fixers:
            did_something = True
            fixers[key](key, errtxt)
    if did_something:
        lisp.flymake_start_syntax_check()

interactions[flymake_fix_current_line] = ''

Finally we have the elisp code that binds a key to the dispatch function.

(pymacs-load "fixers" "fixers-")
(global-set-key "\C-c\C-\\" 'fixers-flymake-fix-current-line)