I have been playing around with the cmd python module and was looking at the text completion function. I have been trying to get it to expand/recognise '~' to my home directory but with no avail.
I've noticed that I can handle default completion by overriding the completedefault(self, *ignored) method from the cmd module. Where ignored is a tuple of the text, line, begidx, endidx. If I type in the command my_command ./folder the text parameter will be './folder' and this mean I can do something like: glob.glob(text + '*') which returns a list of all the files in that folder. However, if I now do my_command ~/folder the text variable now only contains /folder, so I am unable to use os.path.expanduser(text) to determine the absolute path of that folder and show all the files in that folder.
Basically I am wondering if someone could point me in the right direction in order to expand paths with a ~ in it.
Expanding on the answer from: https://stackoverflow.com/a/6657975/1263565
You could override the cmd module's completedefault() method with:
def completedefault(self, *ignored):
# Set the autocomplete preferences
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)
with the complete method looking like:
def complete(text, state):
return (glob.glob(os.path.expanduser(text)+'*')+[None])[state]
This should now allow ~ expansion.
Related
I need to run a different compiler dependent on which version of an app the file being edited is destined to be used with.
The source file is always stored in a path that contains the version number.
So file
%APPDATA%\App\9\Settings\Source.file
Would need to run
%ProgramFiles%\App\9\Compiler.exe "%APPDATA%\App\9\Settings\Source.file"
and
%APPDATA%\App\11\Settings\Source.file
Would need to run
%ProgramFiles%\App\11\Compiler.exe "%APPDATA%\App\11\Settings\Source.file"
I have tried following the advanced example here:
https://www.sublimetext.com/docs/3/build_systems.html#advanced_example
but python really isnt my thing and I can't seem to get anything to run
Basic works but I cant specify the version:
{
"cmd": ["c:\\Program Files\\App\\10\\compile.exe", "$file"],
"selector": "source.app",
"file_patterns": "*.ext"
}
But this doesnt:
{
"target": "app_build",
"selector": "source.app",
"file_patterns": "*.ext"
}
.py file
import sublime
import sublime_plugin
class AppBuildCommand(sublime_plugin.WindowCommand):
def run (self):
vars = self.window.extract_variables()
compiler = vars['file_path']
compiler = compiler.split("\\")
compiler_path = "c:\\Program Files\\App\\" + compiler[compiler.index("App")+1] + "\\Compiler.exe"
file = vars['file']
self.window.run_command (compiler + " \"" + file + "\"")
Also tried with no success:
args = []
args.append(compiler)
args.append(file)
self.window.run_command("cmd", args)
The run_command() method is for executing internal Sublime commands (i.e. the things you would bind to a key or trigger from a menu), so part of the reason why this isn't working for you is that you're trying to get Sublime to run a command instead of having it execute an external program.
A build system is normally executed by the exec command; this is essentially the default value of the target key if you don't provide it in your sublime-build file. exec is responsible for using the arguments given to it by Sublime to start up an external process, capture it's output and display it in the output panel at the bottom of the window.
In order to customize what gets executed, you do need to implement you own WindowCommand and use the target key to tell Sublime to run it, but that command is then responsible for doing what exec would do, which includes starting an external process and capturing the output.
The example that's listed in the documentation uses subprocess.Popen() to perform this task, along with having to track if the task is running to close it, etc.
An easy way to pull this off is to create your command as a subclass of the command that Sublime normally uses to run the build, so that you can customize how the build starts but let existing code take care of all of the details.
An example of such a command would be something like the following:
import sublime
import sublime_plugin
import os
from Default.exec import ExecCommand
class AppBuildCommand(ExecCommand):
def run(self, **kwargs):
# Get the list of variables known to build systems
variables = self.window.extract_variables()
# Is there a file path? There won't be if the file hasn't been saved
# yet.
if "file_path" in variables:
# Pull out the file path, split it into parts, and use the segment
# after the "App" segment as the value of a new variable named
# version.
file_path = variables["file_path"].upper().split(os.sep)
if "APP" in file_path:
variables["version"] = file_path[file_path.index("APP") + 1]
# Expand any remaining variables in our arguments and then execute the
# build.
kwargs = sublime.expand_variables(kwargs, variables)
super().run(**kwargs)
An example of a sublime-build file that uses this command for the build would be:
{
"target": "app_build",
"cancel": {"kill": true},
"cmd": ["c:\\Program Files\\App\\\\${version:100}\\compile.exe", "$file"],
"selector": "source.app",
"file_patterns": ["*.ext"]
}
The command itself examines the path of the current file for the segment that follows the App segment (here case insensitively just in case), and uses that to create a new build system variable named version. If the file hasn't been saved yet (and thus has no name on disk) or if there isn't a path segment named App in it, then the variable is not created.
The last two lines expand any variables that haven't been expanded yet, and then tell the exec command to execute the build.
The sublime-build file can be in every way a normal sublime-build file, but the changes you need to make are:
You need to use target to indicate that our command should be the one to execute
You should (but don't need to) set the cancel key to tell Sublime how to cancel your build if you choose the cancel build command; this one tells Sublime to execute the same app_build command that it used to start the build, but give it an extra argument to say the build should be terminated.
Anywhere you want to access the version number from the path of the file, use the notation \\${version:DEFAULT}.
The variable needs to be specified this way (i.e. with the two \\ characters in front) because Sublime will automatically try to expand any variables in the build system keys that exec recognizes before it calls the command.
Since at the time the command gets called we haven't set the value of the version variable yet, this will make Sublime assume that the value is the empty string, which removes it from the string and stops us from being able to detect that it's there.
In order to get Sublime to leave the variable alone, you need to use the \$ notation as an indication that this should not be treated as a special $ character; Sublime will convert \$ to $ and pass it through to our command. In the sublime-build file you need to use \\$ because \$ is not a valid JSON character escape.
The :DEFAULT portion is optional; this is what will be used as the expanded text if the version variable isn't set. In the example above it's set to 100, but in practice you'd set it to something like 9 or 11 instead. The variable won't be set if the command couldn't figure out the version to use, so you can use this to set a default version to be used in that case (if this makes sense for your use case).
This video series on build systems in Sublime Text has more information in general on how build sytems work, which includes several videos on custom build targets and how they work, including more information on advanced custom targets that subclass the ExecCommand as we're doing here (disclaimer: I am the author).
I've written a program to add directories to the PATH variable via the registry, either the HKCU(user) or HKLM(system) path, depending on an input option.
It works fine when using the User path.
However, when setting the path for the System, Windows acts as if the path variable is empty, e.g.
'notepad' is not recognized as an internal or external command....
However, echo %path% prints everything out appropriately, without any syntax errors. Similarly, if I view the variable in the System Properties GUI, it shows my full path appropriately, e.g.
%SystemRoot%\system32;%SystemRoot%;
Now, if I manually open that variable in the GUI, and add OR remove the trailing semicolon (i.e. make a noticeable but seemingly irrelevant change), then the path seems to work fine.
Yes, I am opening a new command window to check the path. Restarting the machine doesn't seem to do anything either.
Any ideas?
Code excerpt is here:
import _winreg as registry
#HKEY_LOCAL_MACHINE\
SYS_ENV_SUBPATH = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
#HKEY_CURRENT_USER\
USR_ENV_SUBPATH = r"Environment"
def update_reg_path_value(paths_to_add,privilege):
env_key = open_env_registry_key(privilege)
current_path = get_path_from_registry_or_create(env_key)
val_string = create_new_path_value(current_path, paths_to_add)
registry.SetValueEx(env_key,"Path",0,registry.REG_SZ,val_string)
def open_env_registry_key(privilege):
if privilege == 'system':
return registry.OpenKey(registry.HKEY_LOCAL_MACHINE,SYS_ENV_SUBPATH,
0,registry.KEY_ALL_ACCESS)
return registry.OpenKey(registry.HKEY_CURRENT_USER,USR_ENV_SUBPATH,
0,registry.KEY_ALL_ACCESS)
As in the comments, changing REG_SZ to REG_EXPAND_SZ did the trick, as variables using "%" weren't being recognized. This also works when no "%"s exist, so I use it for the user path as well rather than needing to switch between the two.
registry.SetValueEx(env_key,"Path",0,registry.REG_EXPAND_SZ,val_string)
When I try on Windows
webbrowser.open(fname) or os.startfile(fname) or os.system ('cmd /c "start %s"' % fname)
my python script is getting executed.
How to open it for edit in default editor (like SQL script)
Edit:
import ctypes
shell32 = ctypes.windll.shell32
fname = r'C:\Scripts\delete_records.py'
shell32.ShellExecuteA(0,"open",fname,0,0,5)
it opens file explorer at C:\Program Files\ibm\gsk8\lib64\C
Default open and edit actions are handled by ShellExecute WinAPI function (actions are defined in registry in HKEY_CLASSES_ROOT subtree).
There are couple of way to access WinAPI from Python script.
Using nicer wrapper like pywin32. It is safer than ctypes, but it is non-standard Python module. I.e:
import win32api
win32api.ShellExecute(None, "edit", "C:\\Public\\calc.bat", None, "C:\\Public\\", 1)
Using ctypes. It is trickier and doesn't control arguments, so may cause fault (unless you provide result type and arguments type manually).
import ctypes
ShellExecuteA = ctypes.windll.shell32.ShellExecuteA
ShellExecuteA(None, "edit", "C:\\Public\\calc.bat", None, "C:\\Public\\", 1)
To check which actions are supported for desired filetype, do the following:
Run regedit.exe
Go to HKEY_CLASSES_ROOT and pick desired extension, i.e. .py. Read (Default) value on left pane - it would be class name, i.e. Python.File.
Open that class name subtree in HKEY_CLASSES_ROOT. It should contain shell subtree, under which you will find available shell actions. For me and Python scripts they are Edit with IDLE and Edit with Pythonwin.
Pass these values as second parameter of ShellExecute()
"""
Open the current file in the default editor
"""
import os
import subprocess
DEFAULT_EDITOR = '/usr/bin/vi' # backup, if not defined in environment vars
path = os.path.abspath(os.path.expanduser(__file__))
editor = os.environ.get('EDITOR', DEFAULT_EDITOR)
subprocess.call([editor, path])
As per the documentation of os.startfile, you can define an operation to execute. So os.startfile(fname,operation='edit') shoudl work for what you want. See also this answer.
Ok, this thing is driving me crazy right now. So Action 1 Chooses a Folder (I want to save that folder's path as var_1) and Action 3 Selects a File (I want to save this file's path as var_2)
so in the end . . .
var_1 = '/Users/Prometheus/Desktop/'
var_2 = '/Users/Prometheus/Documents/a.txt'
So how do I use these variables and their values inside of Shell Script with python ? I can't use sys.argv because they are set to some weird variables
I usually put 'Ask for Finder Item' > Run Shell Script and then
import sys
variable = open(argv[1]).read()
but i can't use that in this case . my scripts are in python so i'd rather stay in python because i don't know any other language
The Automator variables are only used in the Automator workflow. The variable themselves are not directly accessible to either a shell script or a Python script. The Run Shell Script action allows you to pass the values of particular variables to a shell script in either of two ways: either piping them in through stdin or by passing them as execution arguments. For this sort of use case, the latter is easier. To start with, you need to pick Automator variable names in the Set Value of Variable and Get Value of Variable actions so the values selected can be retained between actions. Here's a very rudimentary workflow example where I've selected two folders:
You might use a Run AppleScript action like this to display the dialogs:
POSIX path of (choose folder default location (path to desktop))
result & linefeed & POSIX path of (choose file default location (path to desktop))
Then set "Pass input" to "to stdin" in the Run Shell Script action and use a script like this:
import sys
folder, file = sys.stdin.read().splitlines()
I am currently using the following code to hook up flymake and Pyflakes in emacs:
(defun flymake-create-temp-in-system-tempdir (filename prefix)
(make-temp-file (or prefix "flymake")))
and then I pass this function to flymake-init-create-temp-buffer-copy. (Taken from http://hustoknow.blogspot.com/2010/09/emacs-and-pyflakes-using-tmp-directory.html).
This code worked fine until yesterday. When I visit certain Python files, I get the following error:
switched OFF Flymake mode for buffer admin.py due to fatal status
CFGERR, warning Configuration error has occured while running
(pyflakes ../../../../../../../tmp/flymake28459SVv)
Why is flymake passing what seems like the wrong filename to pyflakes? I expect it to pass something like "/tmp/efe234234" and I haven't modified any of the tmp directory settings.
I don't recall emacs being updated for Ubuntu recently and can't think of anything that might have caused this to mess up (.emacs files are versioned).
The only issue I can think of is that this is a heavily nested directory symlinked to a directory in my ~/Dropbox directory but this doesn't happen to other symlinked in a similar way.
How can I fix this problem?
UPDATE
I've done some debugging and now I see that it's not passing the correct path as the argument. It needs one more parent directory inserted into the path to make it work which makes me think it's getting messed up because of symlinks.
Here is an example shell session to show what I mean. I am doing this from the correct relative directory:
$ pyflakes ../../../../../tmp/flymake13382xHi
../../../../../tmp/flymake13382xHi: No such file or directory
That is the command flymake is trying to run. If I change it:
$ pyflakes ../../../../../../tmp/flymake13382xHi
I get no output (as expected). Note the extra ".." in the path.
How can I get flymake to pass an absolute path instead of these crazy relative paths?
UPDATE 2
I've gotten everything to work. Basically there is this function:
(defun flymake-pyflakes-init ()
; Make sure it's not a remote buffer or flymake would not work
(when (not (subsetp (list (current-buffer)) (tramp-list-remote-buffers)))
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-in-system-tempdir))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list "pyflakes" (list temp-file)))))
In the last part I had to change the argument to list from local-file to temp-file because local-file was the crazy relative path I didn't want. Why did the author of that snippet use local-file in the first place?
Thanks,
Ryan
For me, i have resolved by modify user script /usr/local/bin/pyflakes, i test if file exist, if no, i add a slash.
#!/bin/bash
FILE=$1
# Test if folder exist
if [ ! -e "$FILE" ]; then
FILE="/$1"
fi
epylint "$FILE" 2>/dev/null
pep8 --ignore=E221,E701,E202 --repeat "$FILE"
true
I don't have a direct solution to the pyflakes question, but here's something similar for you.
I didn't like how C# was handled in flymake, so I modified the C# entry in the flymake-allowed-file-name-masks to refer to distinct init and cleanup routines, with this code:
(let ((csharpentry (assoc "\\.cs\\'" flymake-allowed-file-name-masks)))
(if csharpentry
(setcdr csharpentry '(csharp-flymake-init csharp-flymake-cleanup))
(add-to-list
'flymake-allowed-file-name-masks
(list key 'csharp-flymake-init 'csharp-flymake-cleanup))))
The documentation is sort of spotty on this stuff, but the code is the documentation, and from what I could tell, the init routine is supposed to return the command line for a syntax check run. It is formatted as a 2-element list, the car is the command to run, and the cadr is itself a list of the arguments. In my example the return value is like this:
("csc.exe" ("/t:module" "/nologo" "nameOfFileToCheck.cs"))
The init routine that returns this command line copies the existing buffer into a temporary file, using flymake's builtin copy fn, and then uses the name of the temp file as the thing to check. So if the buffer filename is "source.cs", the actual value returned from the init fn is
("csc.exe" ("/t:module" "/nologo" "source_flymake.cs"))
The code looks like this:
(defun csharp-flymake-init ()
(csharp-flymake-init-impl
'flymake-create-temp-inplace t t 'csharp-flymake-get-cmdline))
(defun csharp-flymake-init-impl (create-temp-f use-relative-base-dir use-relative-source get-cmdline-f)
"Create syntax check command line for a directly checked source file.
Use CREATE-TEMP-F for creating temp copy."
(let* ((args nil)
(temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
(setq args (flymake-get-syntax-check-program-args
temp-source-file-name "."
use-relative-base-dir use-relative-source
get-cmdline-f))
args))
I implemented it in 2 distinct functions, only to follow the pattern I saw in flymake.el.
Anyway...What you can see is that the init fn creates the temp file then returns the arguments. The call to flymake-get-syntax-check-program-args actually boils down to a call into get-cmdline-f, which in the case of C# is csharp-flymake-get-cmdline. Why all this layering and indirection? I don't know; that's how flymake is.
In the C#-specific cleanup fn, I just check for any "product" files from the syntax check and delete them, and then also chain to flymake-simple-cleanup, to delete the *_flymake.cs temp file that was created.
Maybe your nested dir path has something to do with the use-relative-base-dir and use-relative-source arguments that flymake uses in its own default init routine, which I think is flymake-simple-make-init-impl.
The function make-temp-file uses the variable temporary-file-directory, so perhaps you could try setting temporary-file-directory to something else to fix your problem.
This is what I use for PHP on Windows. It uses the make-temp-file suggestion from Trey. It also explicitly sets the init and cleanup functions for PHP, to the things I want. You could use basically the same thing, for python.
(defun cheeso-flymake-create-temp-intemp (file-name prefix)
"Return file name in temporary directory for checking FILE-NAME.
This is a replacement for `flymake-create-temp-inplace'. The
difference is that it gives a file name in
`temporary-file-directory' instead of the same directory as
FILE-NAME.
For the use of PREFIX see that function.
This won't always work; it will fail if the source module
refers to relative paths.
"
(unless (stringp file-name)
(error "Invalid file-name"))
(or prefix
(setq prefix "flymake"))
(let* ((name (concat
(file-name-nondirectory
(file-name-sans-extension file-name))
"_" prefix))
(ext (concat "." (file-name-extension file-name)))
(temp-name (make-temp-file name nil ext))
)
(flymake-log 3 "create-temp-intemp: file=%s temp=%s" file-name temp-name)
temp-name))
(defun cheeso-php-flymake-get-cmdline (source base-dir)
"Gets the cmd line for running a flymake session in a PHP buffer.
This gets called by flymake itself."
(list "c:\\Progra~2\\PHP\\v5.3\\php.exe"
(list "-f" source "-l")))
(defun cheeso-php-flymake-init ()
"initialize flymake for php"
(let ((create-temp-f 'cheeso-flymake-create-temp-intemp)
;;(create-temp-f 'flymake-create-temp-inplace)
(use-relative-base-dir t)
(use-relative-source t)
(get-cmdline-f 'cheeso-php-flymake-get-cmdline)
args
temp-source-file-name)
(setq temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)
args (flymake-get-syntax-check-program-args
temp-source-file-name "."
use-relative-base-dir use-relative-source
get-cmdline-f))
args))
(defun cheeso-php-flymake-cleanup ()
)
(eval-after-load "flymake"
'(progn
;; 1. add a PHP entry to the flymake-allowed-file-name-masks
(let* ((key "\\.php\\'")
(phpentry (assoc key flymake-allowed-file-name-masks)))
(if phpentry
(setcdr phpentry '(cheeso-php-flymake-init cheeso-php-flymake-cleanup))
(add-to-list
'flymake-allowed-file-name-masks
(list key 'cheeso-php-flymake-init 'cheeso-php-flymake-cleanup))))))