I am writing a sublime editor 2 plugin, and would like it to remember a variable for the duration of the session. I do not want it to save the variable as a file (it is a password), but would like to be able to run a command repeatedly, and the variable to be accessible.
I want my plugin to work something like this...
import commands, subprocess
class MyCommand(sublime_plugin.TextCommand):
def run(self, edit, command = "ls"):
try:
thevariable
except NameError:
# should run the first time only
thevariable = "A VALUE"
else:
# should run subsequent times
print thevariable
One way to achieve this would be to make it a global variable. This will allow you to access that variable from any function. Here is a stack question to consider.
Another option would be to add it to the instance of the class. This is commonly done in the __init__() method of the class. This method is run as soon as the class object is instantiated. For more information on self and __init__() consult this stack discussion. Here is a basic example.
class MyCommand(sublime_plugin.TextCommand):
def __init__(self, view):
self.view = view # EDIT
self.thevariable = 'YOUR VALUE'
This will allow you to access this variable from a class object after it has been created. Something like this MyCommandObject.thevariable. These types of variables will last until the window in which the method was called from is closed.
Related
I am currently trying to make some decorators helping users of my software to create code which will inform them about some issues.
Doing classes, I sometimes work with methods which I would like to use only within the class, but not to be called in instance. I know this can be worked out with underscores and dunders, but I don't want to make user's experience a hell, just a little nudge with warning that they used it in scope which is not intended to have such method used.
Let me explain on code block:
class Example:
def __init__(self):
self.sth = 0
def callableMethod(self, is_true):
if is_true:
self.otherMethod()
print (self.sth)
#NoInstanceMethod
def otherMethod(self):
self.sth = 1
Basically what I would like to achieve is that user can create object and use both methods, but when they try to use otherMethod on instance, like that:
i = Example()
i.otherMethod()
I would be able to recognise it and do something to warn the user (through print or logging message, it doesn't matter).
Is there a way to recognise that the method is used on instance in such a way, but not raise the warning on callableMethod (as it is correctly used scope)?
THE PROBLEM
In short, below works but only when hardcoded like this.
class GetWindowCommand(sublime_plugin.WindowCommand):
#NEED VARIABLE INSTEAD OF THE HARDCODED "qv" string
sublime.active_window().run_command("get_window",{"qualifier": "qv"})
def __init__(self,window):
self.window=window
def run(self,qualifier):
self.projectFolders(qualifier)
def projectFolders(self,qualifier):
print(qualifier)
My goal is that when plugin is loaded, it reads the project folders and looks for specific files depending on the folder. As such, I need to access an external variable AS WELL AS the the WindowCommandClass
When doing print(WindowCommandClass) I notice it populates all the methods with the self,window variables and everything works.
In theory I thought I could introduce variables as shown below
qualifier="qv"
print(WindowCommandClass.projectFolders(qualifier))
However, introducing arguments to any method on that class seems to destroy the self and window arguments from the WindowCommandClass. I've only been using python & sublime text api for a couple days, so I have no idea if I'm missing something small or attempting the impossible. Any ideas?
Your question does not make it clear what the problem is. But have a look at this example and the notes I've made in it, perhaps it will help.
class GetWindowCommand(sublime_plugin.WindowCommand):
# The following call should not be anywhere in your plugin at all
# unless for some reason you want to restart your plugin:
# sublime.active_window().run_command("get_window", {"qualifier": "qv"})
def run(self, qualifier=None):
""" Called by Sublime Text when the plugin is run. """
# self.window is set by Sublime - do NOT set it in a __init__() method.
# The 'qualifier' variable will hold whatever the 'qualifier' arg
# was when the plugin was launched, e.g. 'foo' in my example below,
# or None if the plugin was started without the 'qualifier' arg set.
project_data = self.window.project_data()
if not project_data:
return
project_folders = project_data.get("folders", [])
if not project_folders:
print("No project folders have been set.")
return
for folder in project_folders:
print(folder)
You could launch your plugin by assigning a key binding in your user keys file:
{ "keys": ["ctrl+f0"], "command": "get_window", "args": {"qualifier": "foo" } },
In case anyone is looking to pass values to a class in Sublime Text 3 plugin
Given a class that starts out
class GetWindow(sublime_plugin.WindowCommand)
def __init__(self,window):
self.window=window
def results(self)
return("just testing")
I was calling INCORRECTLY as
print(GetWindow().results())
What I had to do was supply the function with the class like this.
print(GetWindow(sublime_plugin.WindowCommand).results())
Now to get the variable as mentioned in original post I can do this
qualifier='qv'
results=SublimeID(sublime.window,qualifier).results()
Also modify class & methods to include the variable
def __init__(self,window,qualifier):
self.qualifier=qualifier
def results(self)
qualifer=self.qualifier
# You can now work with this external variable #
Here is an image showing Python scope activity (version 3.6 and target x64):
Python Scope
The main problem is the relation between both invoke python methods, the first one is used to start the class object, and the second one to access a method of that class. Here is an image of the first invoke python properties:
Invoke Python init method
And the getNumberPlusOne activity call:
Invoke Python getNumberPlusOne method
The python code being executed:
class ExampleClass:
def __init__(self,t,n):
self.text = t
self.number = n
def getNumberPlusOne(self):
return (self.number+1)
And finally, the error when executing the second Invoke Python Method:
An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: Error invoking Python method ----> System.Runtime.Serialization.InvalidDataContractException: Type 'UiPath.Python.PythonObject' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
Any idea about where is the mistake and how to interact with the output object created in the init method?
I believe that this activity was designed with simple scripts in mind, not with entire classes. Here's an article on their Community Forum where user Sergiu.Wittenberger goes into more details.
Let's start with the Load Python Script activity:
In my case the local variable "pyScript" is a pointer to the python object, i.e. an instance of ExampleClass.
Now, there is the Invoke Python Method activity - this one allows us to call a method by name. It seems however that methods on the class are inaccessible to UiPath - you can't just type pyScript.MethodName().
So it seems that we can't access class methods (please proof me wrong here!), but there's a workaround as shown by Sergio. In your case, you would add another method outside your class in order to access or manipulate your object:
class ExampleClass:
def __init__(self,t,n):
self.text = t
self.number = n
def getNumberPlusOne(self):
return (self.number+1)
foo = ExampleClass("bar", 42)
def get_number_plus_one():
return foo.getNumberPlusOne()
Note that this also means that the object is instantiated within the very same file: foo. At this point this seems to be the only option to interact with an object -- again, I'd hope somebody can prove me wrong.
For the sake of completeness, here's the result:
I would like to add to what the above user said that you have to make sure that the imports you use are in the global site-packages, and not in a venv as Studio doesn't have access to that.
Moreoever, always add this:
import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
to the beginning of your code. Again, a limitation of the implementation. (docs here: https://docs.uipath.com/activities/docs/load-script)
Doing this you might be able to do more complicated structures I think, but I haven't tested this out.
name_player = None
health_player = None
inventory_player = []
class engine:
print name_player
I have no idea why this runs without calling it with engine()
The Python interpreter starts by reading your file, one line at a time.
Step 1:
name_player = None adds name_player : None to locals()
Step 2 and 3 proceed in the same way.
Step 4: class engine: Python sees a class and prepares to load the definition into memory. So it's going to read the class and put all of the fields and method definitions into some runtime dictionary probably. In order to do that, it needs to execute the statements in the class.
So normally a class might look like
class Foo():
def my_method():
return "I'm foo!"
This would define a method, and put that definition with the class definition on the heap.
So your definition proceeds as follows. We've started creating the class object and then we come across a statement, so the interpreter executes it. In your case, it's a print statement, so you see it executed.
You'll see now if you call engine(), another print won't happen.
What you probably want is to have this statement in a constructor like so:
class engine:
def __init__(self); #__init__() is a constructor in Python
print name_player
For more information about classes in Python, see https://docs.python.org/2/tutorial/classes.html
When you define a class, python evaluates the statements making up the class's definition. If those statements have side effects, for example sending text to the standard output, then that text will get sent.
If you were to instantiate this, by calling engine(), you would get back an empty object.
I would like to construct a class in python that supports dynamic updating of methods from user supplied source code.
Instances of class Agent have a method go. At the time an instance is constructed, its .go() method does nothing. For example, if we do a=Agent(), and then a.go() we should get a NotImplementedError or something like that. The user then should be able to interactively define a.go() by supplying source code. A simple source code example would be
mySourceString = "print('I learned how to go!')"
which would be injected into a like this
a.update(mySourceString)
Further invokations of a.go() would then result in "I learned how to go!" being printed to the screen.
I have partially figured out how to do this with the following code:
import types
class Error(Exception):
"""Base class for exceptions in this module."""
pass
class NotImplementedError(Error):
pass
class Agent(object):
def go(self):
raise NotImplementedError()
def update(self,codeString):
#Indent each line of user supplied code
codeString = codeString.replace('\n','\n ')
#Turn code into a function called func
exec "def func(self):\n"+' '+codeString
#Make func a bound method on this instance
self.go = types.MethodType(func, self)
QUESTIONS
Is this implementation sensible?
Will this implementation incur unexpected scope issues?
Is there an obvious way to sandbox the user supplied code to prevent it from touching external objects? I can think of ways to do this by supplying sets of allowed external objects, but this seems not pythonic.
Possibly useful SO posts
What's the difference between eval, exec, and compile in Python?
Adding a Method to an Existing Object
(I am working in python 2.6)