Hacking the classpath in jython - classpath

I am using the Jython classPathHacker idiom that I've seen in several places on the internet. The definitive source is:
http://www.jython.org/jythonbook/en/1.0/appendixB.html#using-the-classpath-steve-langer
However, I'm getting the following failure:
TypeError ( getDeclaredMethod(): 2nd arg can't be coerced to java.lang.Class[] )
Here is my code (well mostly SG Langer's code):
class classPathHacker :
##########################################################
# from http://forum.java.sun.com/thread.jspa?threadID=300557
#
# Author: SG Langer Jan 2007 translated the above Java to this
# Jython class
# Purpose: Allow runtime additions of new Class/jars either from
# local files or URL
######################################################
import java.lang.reflect.Method
import java.io.File
import java.net.URL
import java.net.URLClassLoader
import jarray
def addFile (self, s):
#############################################
# Purpose: If adding a file/jar call this first
# with s = path_to_jar
#############################################
# make a URL out of 's'
f = self.java.io.File (s)
u = f.toURL ()
a = self.addURL (u)
return a
def addURL (self, u):
##################################
# Purpose: Call this with u= URL for
# the new Class/jar to be loaded
#################################
parameters = self.jarray.array([self.java.net.URL], self.java.lang.Class)
sysloader = self.java.lang.ClassLoader.getSystemClassLoader()
sysclass = self.java.net.URLClassLoader
print parameters
method = sysclass.getDeclaredMethod("addURL", parameters)
a = method.setAccessible(1)
jar_a = self.jarray.array([u], self.java.lang.Object)
b = method.invoke(sysloader, jar_a)
return u
tmp = classPathHacker()
tmp.addFile("C:\Program Files\Sikuli\libs\mysql-connector-java-3.1.14.jar")
The error is occurring in the method = sysclass.getDeclaredMethod("addURL", parameters) line.

Searching for 2nd arg can't be coerced to java.lang.Class[], I came upon the following solution:
http://python.6.x6.nabble.com/Jython-2-7a2-Issues-with-jarray-and-java-lang-String-Console-prompt-goes-quot-off-quot-td5001336.html
More detail than can be included here, but google answers your question.

Please use this code instead of yours:
import jarray
class classPathHacker(object):
"""Original Author: SG Langer Jan 2007, conversion from Java to Jython
Updated version (supports Jython 2.5.2) From http://glasblog.1durch0.de/?p=846
Purpose: Allow runtime additions of new Class/jars either from
local files or URL
"""
import java.lang.reflect.Method
import java.io.File
import java.net.URL
import java.net.URLClassLoader
def addFile(self, s):
"""Purpose: If adding a file/jar call this first
with s = path_to_jar"""
# make a URL out of 's'
f = self.java.io.File(s)
u = f.toURL()
a = self.addURL(u)
return a
def addURL(self, u):
"""Purpose: Call this with u= URL for
the new Class/jar to be loaded"""
sysloader = self.java.lang.ClassLoader.getSystemClassLoader()
sysclass = self.java.net.URLClassLoader
method = sysclass.getDeclaredMethod("addURL", [self.java.net.URL])
a = method.setAccessible(1)
jar_a = jarray.array([u], self.java.lang.Object)
b = method.invoke(sysloader, [u])
return u
jarLoad = classPathHacker()
a = jarLoad.addFile("..\the\class\path.jar")

Related

Scala 3 macro to list source code of file

I'm developing a library and want to showcase some code in a small website. The code-snippets should be sourced directly from the same project, i.e. the code needs to be self-referencing.
I want to write a macro that, given a file or a classname returns a string with the file contents. How can I achieve this?
I would also accept giving a classname or type as argument
Are you looking for such a macro?
import scala.quoted.*
inline def getContent[A]: String = ${getContentImpl[A]}
def getContentImpl[A: Type](using Quotes): Expr[String] =
import quotes.reflect.*
// for the source specifically of A
// val str = TypeRepr.of[A].typeSymbol.tree.pos.sourceCode.getOrElse(
// report.errorAndAbort("no source code")
// )
// for the whole source file containing the definition of A
val str = TypeRepr.of[A].typeSymbol.pos.getOrElse(
report.errorAndAbort("no symbol position")
).sourceFile.content.getOrElse(
report.errorAndAbort("no source-file content")
)
Expr(str)
// in a different file
getContent[A]
//import Macros.getContent
//
//object Main extends App {
// class A:
// def foo = 1
//
// println(
// getContent[A]
// )
//}

Unable to mock method using pytest-mock

I have a class 'MyClass' with code as below.
class MyClass:
def __init__(update_type_id='1')
self.update_type_id = update_type_id
self._cursor = <database_connection).cursor()
def update_start_dt(self):
self._update_job_ctrl_start_dt()
def _update_job_ctrl_start_dt(self):
update_sql, v_1 = self._get_update_sql(self._update_type_id)
logger.debug(f'Update sql: {update_sql} and v_1: {v_1}')
def _get_update_sql(self, update_type_id: int) -> Tuple:
sql = f"SELECT start_sql, end_sql FROM <database.table> where update_type_key = {self._update_type_id}"
self._run_sql(sql)
record = self._cursor.fetchone()
if record:
return record
else:
logger.error(f'Record Not Found. Update type key ({update_type_id}) not found in the table in the database')
raise Exception
def _run_sql(self, sql_statement: str):
try:
self._cursor.execute(sql_statement)
except (Exception, Error) as e:
logger.error(f'Error {e} encountered when reading from table')
raise e
I am trying to write a test function using pytest-mock which will test the update_start_dt method. The method internally invokes a series of private methods and I am having difficulty in mocking the code which runs through all the private methods. Can anyone help me to understand in what all ways we can mock?
I tried to refer multiple online websites but couldn't get a complete picture.
class TestMyClass:
def test_update_start_dt(mocker,mock_get_connection,mock_run_sql):
mock_manager = mocker.Mock()
mock_get_update_sql = mock_manager.patch('MyClass._get_update_sql')
mock_get_update_sql.return_value = ('123','234')
myclass = MyClass(update_type_id='1')
myclass.update_start_dt()
I am getting error as below for above test code
update_sql, v_1 = self._get_update_sql(self._update_type_id)
ValueError: not enough values to unpack (expected 2, got 0)
The issue here is that you are patching on a Mock object that you are creating, for the purposes of your test you do not need to explicitly create a Mock object. Shown below is how you would test it instead, where we patch directly on the class.
class MyClass:
def __init__(self, update_type_id='1'):
self._update_type_id = update_type_id
self._cursor = None
def update_start_dt(self):
self._update_job_ctrl_start_dt()
def _update_job_ctrl_start_dt(self):
update_sql, v_1 = self._get_update_sql(self._update_type_id)
logger.debug(f'Update sql: {update_sql} and v_1: {v_1}')
def _get_update_sql(self, update_type_id: int):
sql = f"SELECT start_sql, end_sql FROM <database.table> where update_type_key = {self._update_type_id}"
self._run_sql(sql)
record = self._cursor.fetchone()
if record:
return record
else:
logger.error(f'Record Not Found. Update type key ({update_type_id}) not found in the table in the database')
raise Exception
def _run_sql(self, sql_statement: str):
try:
self._cursor.execute(sql_statement)
except (Exception, Error) as e:
logger.error(f'Error {e} encountered when reading from table')
raise e
def test_update_start_dt(mocker):
mock_get_update_sql = mocker.patch.object(MyClass, "_get_update_sql")
mock_get_update_sql.return_value = ("123", "234")
myclass = MyClass(update_type_id='1')
myclass.update_start_dt()
=========================================== test session starts ============================================
platform darwin -- Python 3.8.9, pytest-7.0.1, pluggy-1.0.0
rootdir: ***
plugins: mock-3.7.0
collected 1 item
test_script.py . [100%]
======================================= 1 passed, 1 warning in 0.01s =======================================
Your code would work if you called the Mock object you created instead of the class. That is shown below.
def test_update_start_dt(mocker):
mock_manager = mocker.Mock()
mock_get_update_sql = mock_manager.patch('MyClass._get_update_sql')
mock_get_update_sql.return_value = ('123','234')
# Notice how we use `mock_manager` instead of MyClass
# tests will now pass
myclass = mock_manager(update_type_id='1')
myclass.update_start_dt()
Hopefully you see what the issue is now.

How to inspect implementation with Scala macros

I'd need to find out if a certain method is used in a method implementation. For example, I have this glorious app:
object Test extends App {
def getGreetings = Array.fill(2)("hello")
println(getGreetings.mkString("\n"))
}
I'd like to test if method getGreetings uses the function fill of Array's companion object. The test would succeed with the above implementation and fail for example with:
def getGreetings = Array("hello", "hello") // nah, fill isn't used
With the help of this video I learned that I can inspect an implementation with macros like this:
def printTree(title: String)(expr: Any): Unit = macro printTreeMacro
def printTreeMacro(c: Context)(title: c.Tree)(expr: c.Tree) = {
import c.universe._
val code : String = showCode(expr)
val raw : String = showRaw(expr)
q"""
println(
$title.toUpperCase + "\n\n" +
$code + "\n\n" +
$raw + "\n\n"
)
"""
}
printTree("Method") {
val a = Array.fill(2)("hello")
}
Now printTree reveals that the fill method is used:
Block(List(ValDef(Modifiers(), TermName("a"), TypeTree(),
Apply(Apply(Apply(TypeApply(Select(Select(Ident(scala), scala.Array),
TermName("fill")), List(TypeTree())), List(Literal(Constant(2)))),
List(Literal(Constant("hello")))),
List(Typed(Apply(TypeApply(Select(Ident(scala.reflect.ClassTag),
TermName("apply")), List(TypeTree())),
List(Literal(Constant(String)))), TypeTree()))))), Literal(Constant(())))
The missing piece here is how to do the same for the code of any method, so that I could get the tree for what's inside method getGreetings.
Thanks in advance :)

Scala macro for shortcut

I have defined the following macros to get file, line and object/class from current location:
http://pastebin.com/UsNLemnK
Using SBT, I have defined two projects, in order to compile the macros first, then the actual project using these macros.
The purpose of these macros are to be be used in a log method:
def log( msg: Any, srcFile: String = "", srcLine: String = "", srcClass:String = "")
I am then using this log method as follows:
log(msg, s"$F_",s"$L_",s"$C_")
where F_, L_ and C_ are defined in the macro.
Now, I would like to create a shortcut to avoid this boilerplate and just call:
log(msg)
which should automatically be replaced by
log(msg, s"$F_",s"$L_",s"$C_")
I could define a macro to do this:
def log_(msg: String) : Unit = macro logImpl
def logImpl( c: Context )(msg: c.Expr[String]): c.Expr[Unit] = {
import c.universe._
reify( log(msg.splice, srcFile=s"$F_", srcLine=s"$L_", srcClass=s"$C_") )
}
but again, this macro needs to be compiled before the project, where the log function itself is defined... So I don't see how to solve the compilation dependencies cycle...
Any suggestion about how to do this?
Thanks
Barring the use of macro annotations (which would necessarily and significantly alter your API's syntax), the problem you have to face is that you need the type-checked identifier of your log function.
Since you can't import the entire log implementation, a solution would be to:
wrap the method into a trait,
define this trait in the "macro" project,
add an implicit parameter to the log_ method,
in your "main" project, create an implementation of this trait, and instantiate this implementation in an implicit val visible everywhere you'd like to use the log_ macro (in the package object for example).
Of course, you could also use a simple FunctionN here and avoid the trait definition and implementation, but this way you'll avoid potential conflicts with other same-typed implicits.
In general, your code would resemble the following:
//"macro" project
trait EncapsulatingTrait {
def yourMethod(...)
}
object Macros {
def myMacro(...)(implicit param: EncapsulatingTrait) = macro myMacroImpl
def myMacroImpl( c: Context )(...)
(param: c.Expr[EncapsulatingTrait]): c.Expr[...] = {
import c.universe._
reify(param.splice.yourMethod(...))
}
}
//--------------------------
//"main" project
class Impl extends EncapsulatingTrait {
def yourMethod(...)
}
...
implicit val defaultParam = new Impl
import Macros.myMacro
myMacro(...)
In your specific case, here's how an implementation could look like:
//"macro" project
package yourpackage
import java.io.File
import language.experimental.macros
import scala.reflect.macros.Context
trait LogFunction {
def log( msg: Any, srcFile: String = "", srcLine: Int = -1, srcClass:String = "")
}
object Macros {
// get current line in source code
def L_ : Int = macro lineImpl
def lineImpl( c: Context ): c.Expr[Int] = {
import c.universe._
val line = Literal( Constant( c.enclosingPosition.line ) )
c.Expr[Int]( line )
}
// get current file from source code (relative path)
def F_ : String = macro fileImpl
def fileImpl( c: Context ): c.Expr[String] = {
import c.universe._
val absolute = c.enclosingPosition.source.file.file.toURI
val base = new File( "." ).toURI
val path = Literal( Constant( c.enclosingPosition.source.file.file.getName() ) )
c.Expr[String]( path )
}
// get current class/object (a bit sketchy)
def C_ : String = macro classImpl
def classImpl( c: Context ): c.Expr[String] = {
import c.universe._
val class_ = Literal( Constant( c.enclosingClass.toString.split(" ")( 1 ) ) )
c.Expr[String]( class_ )
}
def log_(msg: String)(implicit logFunc: LogFunction) : Unit = macro logImpl
def logImpl( c: Context )(msg: c.Expr[String])(logFunc: c.Expr[LogFunction]): c.Expr[Unit] = {
import c.universe._
reify( logFunc.splice.log(msg.splice, srcFile=fileImpl(c).splice, srcLine=lineImpl(c).splice, srcClass=classImpl(c).splice) )
}
}
//--------------------------
//"main" project
import yourpackage.LogFunction
class LogImpl extends LogFunction {
def log( msg: Any, srcFile: String = "", srcLine: Int = -1, srcClass:String = "") {
println(List(msg,srcFile,srcLine,srcClass).mkString("|"))
}
}
object testLog {
def main(args: Array[String]): Unit = {
implicit val defaultLog = new LogImpl
import yourpackage.Macros.log_
log_("blah")
}
}
(note that I had to correct the signature of log_ and tweak the macro call a bit)

Difference between object with main() and extends App in scala

I'm working through ScalaInAction (book is still a MEAP, but code is public on github)
Right now I'm in chapter 2 looking at this restClient: : https://github.com/nraychaudhuri/scalainaction/blob/master/chap02/RestClient.scala
First, I setup intelliJ with scala extensions and created a HelloWorld with main():
<ALL the imports>
object HelloWorld {
def main(args: Array[String]) {
<ALL the rest code from RestClient.scala>
}
}
I get the following error when compiling:
scala: forward reference extends over defintion of value command
val httppost = new HttpPost(url)
^
I can fix this by moving the following lines around until the ordering is correct with relation to the def's
require( args.size >= 2, "You need at least two arguments to make a get, post, or delete request")
val command = args.head
val params = parseArgs(args)
val url = args.last
command match {
case "post" => handlePostRequest
case "get" => handleGetRequest
case "delete" => handleDeleteRequest
case "options" => handleOptionsRequest
}
While browsing the github page, I found this: https://github.com/nraychaudhuri/scalainaction/tree/master/chap02/restclient
Which uses implements RestClient.scala using extends App instead of a main() method:
<All the imports>
object RestClient extends App {
<All the rest of the code from RestClient.scala>
}
I then changed my object HelloWorld to just use extends App instead of implementing a main() method and it works without errors
Why does the main() method way of doing this generate the error but the extends App does not?
Because main() is a method, and variable's in method could not be forward reference.
For example:
object Test {
// x, y is object's instance variable, it could be forward referenced
def sum = x + y // This is ok
val y = 10
val x = 10
}
But code in a method could not forward referenced.
object Test {
def sum = {
val t = x + y // This is not ok, you don't have x, y at this point
val x = 10
val y = 20
val z = x + y // This is ok
}
}
In your case, if you copy paste all codes from RestClient.scala to main(), you will have the same issue because var url is declared after its usage in handlePostRequest.