How can I make these functions work following OOP Principles? - python

I have several functions/methods in a class that are kind of connected. I am building a class that mimics terminal commands and links. However, someone told me this is not proper OOP. How can I separate these methods to work independently. Methods shouldn't call other methods. Correct?
class directory:
#FILES, LINKS AND DIRECTORIES
current_path = []
hold_files = [
'test1.txt', 'test2.py',
{'/desktop': ['computer.txt','tfile.doc',
{'/peace':{
'/pictures': [
'hello.gif',
'smile.gif',
'run.gif'
]},
'/work':[
'file1.txt',
'file2.txt'
]
}]
}
]
#recursively delete folder (if dot in)
def delete(itself):
#if dictionary, call self, else delete
del itself
return
## HELPER METHODS
# Join list together to produce new link, basically return the added folder to the link
def concatenate(self):
new_link ="".join(current_path)
return new_link
#strip slashes and place in list
def adjust_link(self, paths):
new_string = ""
# shorten link, if someone uses cd .., basically go back to previous folder
if paths == "cd ..":
current_path.pop()
#extend link, if someone is cding into another folder, remove /'s and append to separate list
elif "cd " in paths:
paths = paths[3:]
for slash in paths:
if slash == "/":
current.append(new_string)
new_string = ""
else:
new_string+=slash
# This shouldn't be here as OOP must be separated but this calls the other function to concatenate a new link
stripped = concatenate()
return stripped
#returns link
def link(self, paths):
address_location = adjust_link(paths)
return address_location
directory.link("cd desktop/peace")
directory.link("cd pictures")
directory.link("cd ..")
directory.delete()
Thank you.
*Also, this is not a refactoring question. I already asked on stack exchange code review and they told me to come here. Code does not work.
Edit 2: why won't "directory.link()" work?

"I have a program here and I want to convert it to OOP" is not usually how it's done: "I have a problem I want to solve using OOP" is usually the approach. It looks like you are creating something that will traverse an internal directory structure. So a skeleton might look like:
class DirectoryTraverser:
def __init__(self, directory_tree):
self.hold_files = directory_tree
self.current_path = []
def... # all your other functions
# then to use it might look like:
# create a directory traversal object with directory tree
dt = DirectoryTraverser(hold_files)
dt.link("cd desktop/peace")
dt.link("cd pictures")
dt.link("cd ..")
dt.delete()

Related

How to clean a file path?

Is there a cleaner, shorter, perhaps more elegant or Pythonic way to do this?
Unique constraint: The same code has to work on both Python 3.x and MicroPython. This means that a lot of the nice tools available in Python 3.x cannot be used. Hence the "back to basics" feel to the code.
It attempts to clean-up some malformed input. That's not super important. Also, yes, it has to behave on both Windows and Linux. I tested on Windows and MicroPython on an RP2040 processor (RaspberryPi Pico). Works fine.
Warning: If you run this code on your system it will create over twenty directories. Make sure you run it on a junk directory.
import os
def create_path(path:str) -> str:
"""Given a path with a file name, create any missing directories
Does NOT create files.
:param path: "some/subdirectory/file.txt"
:return: The new full path, ready to open the file
"""
result = os.getcwd()
# Cleaning up an leading and multiple slashes
while "//" in path:
path = path.replace("//", "/")
if len(path) > 0:
path = path if path[0] != "/" else path[1:]
elements = path.split("/")
for element in elements:
if element == "":
break
else:
result += "/" + element
if "." in element:
break # It's a file; we are done
else:
# It's a directory, does it exist?
try:
os.listdir(result)
except:
# It does not, create it
os.mkdir(result)
# This is necessary to remove a leading double slash
# when used in MicroPython
return result.replace("//", "/")
if __name__ == "__main__":
# Tests
# WARNING: This will create over twenty directories!
# Does not create the file, just the path
print(create_path("/"))
print(create_path("//"))
print(create_path("///"))
print(create_path("file_01.txt"))
print(create_path("/file_02.txt"))
print(create_path("//file_03.txt"))
print(create_path("///file_04.txt"))
print(create_path("dir_00"))
print(create_path("/dir_01"))
print(create_path("//dir_02"))
print(create_path("dir_03/"))
print(create_path("dir_04//"))
print(create_path("dir_05///"))
print(create_path("/dir_06"))
print(create_path("/dir_07/"))
print(create_path("//dir_08/"))
print(create_path("//dir_09//"))
print(create_path("dir_10/file_05.txt"))
print(create_path("/dir_11/file_06.txt"))
print(create_path("//dir_12/file_07.txt"))
print(create_path("//dir_13//file_08.txt"))
print(create_path("//dir_14//file_09.txt/"))
print(create_path("//dir_15//file_10.txt//"))
print(create_path("dir_16/dir_116/file_11.txt"))
print(create_path("/dir_17/dir_117/file_12.txt"))
print(create_path("//dir_18/dir_118/file_13.txt"))
print(create_path("//dir_19///dir_119/file_14.txt"))
print(create_path("//dir_20///dir_120///file_15.txt"))
print(create_path("//dir_21//dir_121//file_16.txt/"))
print(create_path("//dir_22//dir_122//file_17.txt/"))

How to solve class objecto has no atribute

beginner Python user here.
So, I´m trying to make a program that orders the files of my (many) Downloads folder.
I made a class object to work with the many folders:
class cContenedora:
def __int__(self, nCarp, dCarp): #nCarp Stands is the file name and dCarp Stands for file directory.
self.nCarp = nCarp
self.dCarp = dCarp
So, y wrote a instance like this:
Download = cContenedora()
Download.nCarp = "Downloads/"
#The side bar is for making a path to move my archives from with shutil.move(path, dest)
Download.dCarp = "/Users/MyName/Download/"
#This is for searching the folder with os.listdir(Something.dCarp)
Then, I wrote my function, and it goes something like this:
def ordenador(carpetaContenedora, formato, directorioFinal): #carpetaContenedora is a Download Folder
carpetaContenedora = cContenedora() #carpetaContenedora one of the class objects
dirCCont = os.listdir(carpetaContenedora.dCarp) #The to directory is carpetaContenedora.cCarp
for a in dirCCont:
if a.endswith(formato):
path = "/Users/Aurelio Induni/" + carpetaContenedora().nCarp + a
try:
shutil.move(path, directorioFinal)
print(Fore.GREEN + a + "fue movido exitosamente.")
except:
print(Fore.RED + "Error con el archivo" + a)
pass
for trys in range(len(listaCarpetasDestino)-1): #Is a list full of directories.
for container in listaCarpetasFuente: #A short list of all my Downloads Folder.
for formatx in listaFormatos: #listaFormatos is a list ful of format extensions like ".pdf"
#try: #I disabled this to see the error istead of "Error Total"
ordenador(container, formatx, listaCarpetasDestino[trys])
#except:
#print(Fore.RED + "Error Total") #I disabled this to see the error.
But every time I run it I get the following:
AttributeError: 'cContenedora' object has no attribute 'dCarp'
It says the error is in line 47 (the one with the os.listdir(carpetaContenedora.dCarp))
I´m sure is something small. Python is so amazing, but it also can be so frustrating not knowing what´s wrong.
There is a spelling mistake in the initialization of your instance. It should be "init" instead of "int".
In the class cContenedora, the function should be
class cContenedora:
def __init__(self, nCarp, dCarp):
self.nCarp = nCarp
self.dCarp = dCarp
Additionally, When you are passing in the parameter. Make sure to pass in both of the parameters in the line with Value.
CContenedora(nCarp="something",dCarp="something")
Your class initializer, i.e., __init__() function has 2 parameters nCarp and dCarp but when you are actually creating the object there are no parameters passed.
Your function ordenador takes the first parameter as carpetaContenedora, on the first line same variable is assigned a new object of cContenedora, at this line the original values you passed are lost forever.
This could be the reason it is giving for the error.
Refer this link for more details on how to create classes and instantiate the object.

Why my function isn't stopping at "return" line?

I'm trying to write a function that will search for "Searched" directory in the directory tree and return path to it, it should stop when the directory is found, but it isn't, where is my mistake?
import os
searched = "NodeBook"
def find(Path, searched):
print("Searching in " + os.path.normpath(Path))
for filePath in os.listdir(Path):
if ((filePath == searched) and (os.path.isdir(os.path.join(Path, filePath)))) :
print("Found")
print(filePath)
print(os.path.join(Path, filePath))
return os.path.join(Path, filePath)
elif (os.path.isdir(filePath)) :
find(os.path.join(Path, filePath), searched)
find( "./", searched)
I expect something like that :
Searching in .
Searching in nodeLearning
Searching in nodeParse
Searching in Screeps
Found
NodeBook
But i have :
Searching in .
Searching in nodeLearning
Searching in nodeParse
Searching in Screeps
Found
NodeBook
./Screeps\NodeBook
Searching in testpython
Searching in testReact
Searching in testReact\testreact
It goes through all subdirectories.
You have a few small issues.
Bug 1: you look at isdir(filePath) instead of isdir(os.path.join(Path, filePath)). This can cause errors if you have a file that is not a directory with the same name as a directory in your starting location. For example
/tmp/a <-- dir
/tmp/b <-- dir
/tmp/b/a <-- file
would give an OSError
Bug 2: You don't stop if you find a match in a recursive call
You can fix this in a variety of ways, chose to do this by checking the return in your recursive call.
Bug 3: I think this may go forever if it encounters symlinks that form a loop. Didn't fix, but you should decide how you would handle it.
I also renamed a few things for clarity.
import os
def find_subdir(base_dir, search):
print("Searching in " + os.path.normpath(base_dir))
for name in os.listdir(base_dir):
path = os.path.join(base_dir, name)
if not os.path.isdir(path):
continue
if name == search:
return path
sub_search = find_subdir(path, search)
if sub_search is not None:
return sub_search
return None # For clarity
result = find_subdir( "./", "NodeBook")
if result is not None:
print("Found")
print(result)
Here the function is calling itself:
elif (os.path.isdir(filePath)) :
find(...)
Okay, but this is happening in a loop, so after this call returns, the loop will continue. You should rethink the logic: maybe you can check the return value and then either return it if it indicates a valid path, or continue looping otherwise.
For example, right now the function returns None when nothing has been found, so you can check if the return value is None:
ret = find(...)
if ret is not None:
return ret
# continue looping otherwise

Get file syntax selection in sublime text 3 plugin

I have a very small plugin to open a perl file module starting from the use statement. It's really basic and it just replaces '::' with '/' and then if the file exists in one of the paths specified in PERL5LIB, it opens it.
I want it to run only when the open file syntax is selected as perl.
Is there any API to get that information?
This is the code that I have now:
class OpenPerlModule(sublime_plugin.TextCommand):
def run(self, edit=None, url=None):
perl_file = url.replace("::", "/")
perl_dirs = os.environ.get('PERL5LIB')
for perl_dir in perl_dirs.split(':'):
if (os.path.exists(perl_dir + '/' + perl_file + '.pm')):
self.view.window().open_file(perl_dir + '/' + perl_file + '.pm')
return
(The OS is Ubuntu)
Here is the code snippet you're looking for
self.view.settings().get("syntax")
You should check whether it's a syntax related to Perl or not. I suggest something like this:
syntax = self.view.settings().get("syntax")
syntax.endswith("Perl.tmLanguage") or syntax.endswith("Perl.sublime-syntax")
The second or clause is to cover the new syntax that's introduced in >=3080
Aside from self.view.settings().get("syntax") as described in Allen Bargi's answer, you could also get the scope for the current cursor position and check for source.perl in it:
import sublime_plugin
class FindScopeCommand(sublime_plugin.TextCommand):
def run(self, edit):
# `sel()` returns a list of Regions that are selected.
# Grab the beginning point of the first Region in the list.
first_point = self.view.sel()[0].a
# now, get the full scope name for that point
scope = self.view.scope_name(first_point)
if "source.perl" in scope:
print("You're using Perl. Yay!")
else:
print("Why don't you love Perl?")

Substitutions inside links in reST / Sphinx

I am using Sphinx to document a webservice that will be deployed in different servers. The documentation is full of URL examples for the user to click and they should just work. My problem is that the host, port and deployment root will vary and the documentation will have to be re-generated for every deployment.
I tried defining substitutions like this:
|base_url|/path
.. |base_url| replace:: http://localhost:8080
But the generated HTML is not what I want (doesn't include "/path" in the generated link):
http://localhost:8080/path
Does anybody know how to work around this?
New in Sphinx v1.0:
sphinx.ext.extlinks – Markup to shorten external links
https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
The extension adds one new config value:
extlinks
This config value must be a dictionary of external sites, mapping unique short alias names to a base URL and a prefix. For example, to create an alias for the above mentioned issues, you would add
extlinks = {'issue':
('http://bitbucket.org/birkenfeld/sphinx/issue/%s', 'issue ')}
Now, you can use the alias name as a new role, e.g. :issue:`123`. This then inserts a link to http://bitbucket.org/birkenfeld/sphinx/issue/123. As you can see, the target given in the role is substituted in the base URL in the place of %s.
The link caption depends on the second item in the tuple, the prefix:
If the prefix is None, the link caption is the full URL.
If the prefix is the empty string, the link caption is the partial URL given in the role content (123 in this case.)
If the prefix is a non-empty string, the link caption is the partial URL, prepended by the prefix – in the above example, the link caption would be issue 123.
You can also use the usual “explicit title” syntax supported by other roles that generate links, i.e. :issue:`this issue <123>`. In this case, the prefix is not relevant.
I had a similar problem where I needed to substitute also URLs in image targets.
The extlinks do not expand when used as a value of image :target: attribute.
Eventually I wrote a custom sphinx transformation that rewrites URLs that start with a given prefix, in my case, http://mybase/. Here is a relevant code for conf.py:
from sphinx.transforms import SphinxTransform
class ReplaceMyBase(SphinxTransform):
default_priority = 750
prefix = 'http://mybase/'
def apply(self):
from docutils.nodes import reference, Text
baseref = lambda o: (
isinstance(o, reference) and
o.get('refuri', '').startswith(self.prefix))
basetext = lambda o: (
isinstance(o, Text) and o.startswith(self.prefix))
base = self.config.mybase.rstrip('/') + '/'
for node in self.document.traverse(baseref):
target = node['refuri'].replace(self.prefix, base, 1)
node.replace_attr('refuri', target)
for t in node.traverse(basetext):
t1 = Text(t.replace(self.prefix, base, 1), t.rawsource)
t.parent.replace(t, t1)
return
# end of class
def setup(app):
app.add_config_value('mybase', 'https://en.wikipedia.org/wiki', 'env')
app.add_transform(ReplaceMyBase)
return
This expands the following rst source to point to English wikipedia.
When conf.py sets mybase="https://es.wikipedia.org/wiki" the links would point to the Spanish wiki.
* inline link http://mybase/Helianthus
* `link with text <http://mybase/Helianthus>`_
* `link with separate definition`_
* image link |flowerimage|
.. _link with separate definition: http://mybase/Helianthus
.. |flowerimage| image:: https://upload.wikimedia.org/wikipedia/commons/f/f1/Tournesol.png
:target: http://mybase/Helianthus
Ok, here's how I did it. First, apilinks.py (the Sphinx extension):
from docutils import nodes, utils
def setup(app):
def api_link_role(role, rawtext, text, lineno, inliner, options={},
content=[]):
ref = app.config.apilinks_base + text
node = nodes.reference(rawtext, utils.unescape(ref), refuri=ref,
**options)
return [node], []
app.add_config_value('apilinks_base', 'http://localhost/', False)
app.add_role('apilink', api_link_role)
Now, in conf.py, add 'apilinks' to the extensions list and set an appropriate value for 'apilinks_base' (otherwise, it will default to 'http://localhost/'). My file looks like this:
extensions = ['sphinx.ext.autodoc', 'apilinks']
# lots of other stuff
apilinks_base = 'http://host:88/base/'
Usage:
:apilink:`path`
Output:
http://host:88/base/path
You can write a Sphinx extension that creates a role like
:apilink:`path`
and generates the link from that. I never did this, so I can't help more than giving this pointer, sorry. You should try to look at how the various roles are implemented. Many are very similar to what you need, I think.

Categories