How do I rename a file in Scala? - scala

I want to rename a file in the system by Scala code. The equivalent of what can be done by bash like,
mv old_file_name new_file_name
I am not asking about renaming a scala source code file, but a file residing in the system.

Consider
import java.io.File
import util.Try
def mv(oldName: String, newName: String) =
Try(new File(oldName).renameTo(new File(newName))).getOrElse(false)
and use it with
mv("oldname", "newname")
Note mv returns true on successful renaming, false otherwise. Note also that Try will catch possible IO exceptions.

See renameTo of java.io.File. In your case this would be
new File("old_file_name").renameTo(new File("new_file_name"))

Use Guava:
Files.move(new File("<path from>"), new File("<path to>"))

Related

How to run gsutil from Scala without the .cmd suffix?

I am trying to run gsutil in Scala, but it doesn't work unless I explicitly put .cmd in the code. I don't like this approach, since others I work with use Unix systems. How do I let Scala understand that gsutil == gsutil.cmd? I could just write a custom shell script and add that to path, but I'd like a solution that doesn't include scripting.
I have already tried with various environment variables (using IntelliJ, don't know if it's relevant). I have tried adding both /bin and /platform/gsutil to path, neither works (without .cmd at least). I have also tried giving full path to see if it made a difference, it didn't.
Here is the method that uses gsutil:
def readFilesInBucket(ss: SparkSession, bucket: String): DataFrame = {
import ss.implicits._
ss.sparkContext.parallelize((s"gsutil ls -l $bucket" !!).split("\n")
.map(r => r.trim.split(" ")).filter(r => r.length == 3)
.map(r => (r(0), r(1), r(2)))).toDF(Array("Size", "Date", "File"): _*)
}
This is my first ever question on SO, I apologize for any formattic errors there may be.
EDIT:
Found out, that even when I write a script like this:
exec gsutil.cmd "$#"
called just gsutil in the same folder, it spits out the same error message as before: java.io.IOException: Cannot run program "gsutil": CreateProcess error=2, The system cannot find the file specified.
It works if I write gsutil in git bash, which otherwise didn't work without the script.
Maybe just use a different version whether you're on Windows or *nix system?
Create some helper:
object SystemDetector {
lazy val isWindows = System.getProperty("os.name").startsWith("Windows")
}
And then just use it like:
def readFilesInBucket(ss: SparkSession, bucket: String): DataFrame = {
import ss.implicits._
val gsutil = if(SystemDetector.isWindows) "gsutil.cmd" else "gsutil"
ss.sparkContext.parallelize((s"$gsutil ls -l $bucket" !!).split("\n")
.map(r => r.trim.split(" ")).filter(r => r.length == 3)
.map(r => (r(0), r(1), r(2)))).toDF(Array("Size", "Date", "File"): _*)
}

pytest implementing a logfile per test method

I would like to create a separate log file for each test method. And i would like to do this in the conftest.py file and pass the logfile instance to the test method. This way, whenever i log something in a test method it would log to a separate log file and will be very easy to analyse.
I tried the following.
Inside conftest.py file i added this:
logs_dir = pkg_resources.resource_filename("test_results", "logs")
def pytest_runtest_setup(item):
test_method_name = item.name
testpath = item.parent.name.strip('.py')
path = '%s/%s' % (logs_dir, testpath)
if not os.path.exists(path):
os.makedirs(path)
log = logger.make_logger(test_method_name, path) # Make logger takes care of creating the logfile and returns the python logging object.
The problem here is that pytest_runtest_setup does not have the ability to return anything to the test method. Atleast, i am not aware of it.
So, i thought of creating a fixture method inside the conftest.py file with scope="function" and call this fixture from the test methods. But, the fixture method does not know about the the Pytest.Item object. In case of pytest_runtest_setup method, it receives the item parameter and using that we are able to find out the test method name and test method path.
Please help!
I found this solution by researching further upon webh's answer. I tried to use pytest-logger but their file structure is very rigid and it was not really useful for me. I found this code working without any plugin. It is based on set_log_path, which is an experimental feature.
Pytest 6.1.1 and Python 3.8.4
# conftest.py
# Required modules
import pytest
from pathlib import Path
# Configure logging
#pytest.hookimpl(hookwrapper=True,tryfirst=True)
def pytest_runtest_setup(item):
config=item.config
logging_plugin=config.pluginmanager.get_plugin("logging-plugin")
filename=Path('pytest-logs', item._request.node.name+".log")
logging_plugin.set_log_path(str(filename))
yield
Notice that the use of Path can be substituted by os.path.join. Moreover, different tests can be set up in different folders and keep a record of all tests done historically by using a timestamp on the filename. One could use the following filename for example:
# conftest.py
# Required modules
import pytest
import datetime
from pathlib import Path
# Configure logging
#pytest.hookimpl(hookwrapper=True,tryfirst=True)
def pytest_runtest_setup(item):
...
filename=Path(
'pytest-logs',
item._request.node.name,
f"{datetime.datetime.now().strftime('%Y%m%dT%H%M%S')}.log"
)
...
Additionally, if one would like to modify the log format, one can change it in pytest configuration file as described in the documentation.
# pytest.ini
[pytest]
log_file_level = INFO
log_file_format = %(name)s [%(levelname)s]: %(message)
My first stackoverflow answer!
I found the answer i was looking for.
I was able to achieve it using the function scoped fixture like this:
#pytest.fixture(scope="function")
def log(request):
test_path = request.node.parent.name.strip(".py")
test_name = request.node.name
node_id = request.node.nodeid
log_file_path = '%s/%s' % (logs_dir, test_path)
if not os.path.exists(log_file_path):
os.makedirs(log_file_path)
logger_obj = logger.make_logger(test_name, log_file_path, node_id)
yield logger_obj
handlers = logger_obj.handlers
for handler in handlers:
handler.close()
logger_obj.removeHandler(handler)
In newer pytest version this can be achieved with set_log_path.
#pytest.fixture
def manage_logs(request, autouse=True):
"""Set log file name same as test name"""
request.config.pluginmanager.get_plugin("logging-plugin")\
.set_log_path(os.path.join('log', request.node.name + '.log'))

going to specific path in scala using scala.sys.process

I have to go to the path of an application to deploy it, I tried using scala.sys.process and did "cd /home/path/somepath" !
It is throwing an exception, Can anyone guide me how I can go to the directory, I cannot deploy it using absolute path because of the dependency the run file has.
Thanks in advance
Although this question is a couple of years old, it's a good question.
To use scala.sys.process to execute something from a specific working directory, pass the required directory as a parameter to ProcessBuilder, as in this working example:
import scala.sys.process._
val scriptPath = "/home/path/myShellScript.sh"
val command = Seq("/bin/bash","-c",scriptPath)
val proc = Process(command,new java.io.File("."))
var output = Vector.empty[String]
val exitValue = proc ! ProcessLogger (
(out) => if( out.trim.length > 0 )
output +:= out.trim,
(err) =>
System.err.printf("e:%s\n",err) // can be quite noisy!
)
printf("exit value: %d\n",exitValue)
printf("output[%s]\n",output.mkString("\n"))
If the goal instead is to insure that the environment of the caller defaults to a specific working directory, that can be accomplished by setting the required working directory before launching the jvm.

Docopt and Classes

Ive gotten quite comfortable over the last year coding in python on an off, but I have stayed away from Classes (as in structuring my code in them) because I have not understood them.
I am now trying to get my head around what I need to change in my coding practices to take advantage of using Classes in all their glory.
I have been trying to use an example script I wrote and pipe that to a Class based version. Safe to say I am sucking bad and cant get my simple script to work. Im sure there are a myriad of this Im most likely doing incorrectly. I would really appreciate someone pointing them out to me.
I dont mind finger points and belly laughs too ^_^
Coder After (not working)
"""
Description:
This script is used to walk a directory and print out each filename and directory including the full path.
Author: Name
Usage:
DirLister.py (-d <directory>)
DirLister.py -h | --help
DirLister.py --version
Options:
-d <directory> The top level directory you want to list files and directories from.
-h --help Show this screen.
--version Show version.
"""
import os
from docopt import docopt
class walking:
def __init__(self, directory):
self.directory = arguments['-d']
def walk(self, directory):
for root, dirs, files in os.walk(self.directory):
for filename in files:
print os.path.join(root, filename)
if __name__ == '__main__':
arguments = docopt(__doc__, version= '1.0.0')
print arguments
if arguments['-d'] is None:
print __doc__
exit(0)
else:
walking.walk(directory)
Original Non-Class Based Code (working)
"""
Description:
This script is used to walk a directory and print out each filename and directory including the full path.
Author: Name
Usage:
DirLister.py (-d <directory>)
DirLister.py -h | --help
DirLister.py --version
Options:
-d <directory> The top level directory you want to list files and directories from.
-h --help Show this screen.
--version Show version.
"""
import os
from docopt import docopt
arguments = docopt(__doc__, version= '1.0.0')
def walk(dir):
for root, dirs, files in os.walk(dir):
for filename in files:
print os.path.join(root, filename)
if __name__ == '__main__':
if arguments['-d'] is None:
print __doc__
exit(0)
else:
walk(arguments['-d'])
You've forgotten to post the error you get (since you say it's not working).
But indeed there are several issues. First, I'd call the class Walking.
Then in your __init__ function, you try to access arguments which is neither a global variable nor an argument; you wanted to write:
def __init__(self, directory):
self.directory = directory
But you also need to create an instance of your class in you main:
walking = Walking(arguments['-d'])
That assumes that the name of the class is Walking instead of walking. I advise you to look at PEP8 for the naming conventions.
The general idea is that the class is the type of an object, but not the object itself*, so the class Walking: block is basically defining a new kind of objects. And then you can create objects that are instances of this class. It's the same when you create a list: mylist = list() (but there are also other ways for lists like mylist = [1, 2]).
*It happens that most things in Python are objects, including classes, but they have obviously other methods and they have another base class.

How do you write a Scala script that will react to file changes

I would like to change the following batch script to Scala (just for fun), however, the script must keep running and listen for changes to the *.mkd files. If any file is changed, then the script should re-generate the affected doc. File IO has always been my Achilles heel...
#!/bin/sh
for file in *.mkd
do
pandoc --number-sections $file -o "${file%%.*}.pdf"
done
Any ideas around a good approach to this will be appreciated.
The following code, taken from my answer on: Watch for project files also can watch a directory and execute a specific command:
#!/usr/bin/env scala
import java.nio.file._
import scala.collection.JavaConversions._
import scala.sys.process._
val file = Paths.get(args(0))
val cmd = args(1)
val watcher = FileSystems.getDefault.newWatchService
file.register(
watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE
)
def exec = cmd run true
#scala.annotation.tailrec
def watch(proc: Process): Unit = {
val key = watcher.take
val events = key.pollEvents
val newProc =
if (!events.isEmpty) {
proc.destroy()
exec
} else proc
if (key.reset) watch(newProc)
else println("aborted")
}
watch(exec)
Usage:
watchr.scala markdownFolder/ "echo \"Something changed!\""
Extensions have to be made to the script to inject file names into the command. As of now this snippet should just be regarded as a building block for the actual answer.
Modifying the script to incorporate the *.mkd wildcards would be non-trivial as you'd have to manually search for the files and register a watch on all of them. Re-using the script above and placing all files in a directory has the added advantage of picking up new files when they are created.
As you can see it gets pretty big and messy pretty quick just relying on Scala & Java APIs, you would be better of relying on alternative libraries or just sticking to bash while using INotify.