Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abstract out scala wrappers and other utils library into a separate package #144

Open
KOLANICH opened this issue Aug 11, 2020 · 0 comments

Comments

@KOLANICH
Copy link

Hi. I am using JPype + some scala package (third-party app, I know almost nothing about scala) for pretty a long time. But it was always a pain to deal with scala stuff, since it does everything itself and in a very strange way. So I just took Krakatau decompiler, written snippets in scala (mostly taken from StackOverflow, but modified and simplified), decompiled them and tried to incorporate the generated code into my app.

It worked and with a lot of pain and debugging I got a working app. Untill the upstream (or maybe scala authors) has changed something in the app and I started getting very nasty errors, like

class scala.collection.convert.Wrappers$IterableWrapper cannot be cast to class java.lang.String (scala.collection.convert.Wrappers$IterableWrapper is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap').

Could you:

  1. modularize the parts related to Scala into a separate package.
  2. document your understanding how the conversion of java types into scala ones work and how to fix the typical bugs?
Here is my code
import re
from JAbs import SelectedJVMInitializer, ClassPathT, ClassesImportSpecT  #abstraction layer, allows to use not only with JPype, but also with GraalVM
from collections import OrderedDict, defaultdict

import sys

class ScalaJVMInitializer:
	__slots__ = ("ji",)
	def __init__(self, classPathz: ClassPathT, classes2import: ClassesImportSpecT) -> None:
		self.__class__.ji.__set__(self, SelectedJVMInitializer(classPathz, classes2import))
		self.loadScala()


	def __getattr__(self, k):
		return getattr(self.ji, k)

	def __setattr__(self, k, v):
		setattr(self.ji, k, v)

	def loadScala(self) -> None:
		self.ji.loadClasses(( # this method of imports classes into ji, if tuple, the secind item is name
			"scala.concurrent.Await",
			"scala.collection.mutable.Seq",
			"scala.collection.JavaConverters",
			("scala.Predef$", "scalaPredef"),
			("scala.collection.Seq$", "scalaCollSeq"),
			"java.util.Arrays"
		))
		self.scalaPredef = getattr(self.scalaPredef, "MODULE$")
		self.scalaCollSeq = getattr(self.scalaCollSeq, "MODULE$")


	def scalaSeq(self, it):
		print("it", it)
		coll = self.scalaCollSeq.apply(self.scalaPredef.wrapRefArray(list(it)))
		coll = coll.to(self.scalaCollSeq.canBuildFrom())
		print("coll", coll)
		print("coll.__class__.__name__", coll.__class__.__name__)
		return coll
		#return self.scalaCollSeq.apply(self.scalaPredef.wrapRefArray(list(it)))
		#return self.scalaPredef.wrapRefArray(list(it))
		#return self.JavaConverters.collectionAsScalaIterable(self.Arrays.asList(list(it))).toSeq()

	scalaTupleRx = re.compile("^_(\\d+)$")

	@classmethod
	def scalaDetuple(cls, t):
		res = [None] * t.productArity()
		for n in dir(t):
			m = cls.scalaTupleRx.match(n)
			if m is not None:
				res[int(m.group(1)) - 1] = getattr(t, n)()
		return tuple(res)


	@staticmethod
	def getSomeKindOfImmutableObjectTemplate(cls):
		c = max(cls.class_.getConstructors(), key=lambda ct: len(ct.getParameters()))
		return OrderedDict([(str(p.getName()), None) for p in c.getParameters()])

	@classmethod
	def scalaSomeKindOfImmutableObjectToDict(cls, o, template=None):
		if template:
			d = type(template)(template)
		else:
			d = cls.getSomeKindOfImmutableObjectTemplate(o.__class__)

		for p in d.keys():
			d[p] = getattr(o, p)()
		return d


	@classmethod
	def mergeScalaSomeKindOfImmutableObjects(cls, defaultOne, *args, subMerge=None):
		res = cls.getSomeKindOfImmutableObjectTemplate(defaultOne.__class__)
		if subMerge is None:
			subMerge = {}
		scheduledSubMerges = defaultdict(list)
		defaultOne = cls.scalaSomeKindOfImmutableObjectToDict(defaultOne, res)
		res = type(defaultOne)(defaultOne)

		for i, o in enumerate(args):
			if not isinstance(o, dict):
				o = cls.scalaSomeKindOfImmutableObjectToDict(o, res)
			for k, v in o.items():
				#print(i, k, v)
				if k in subMerge:
					scheduledSubMerges[k].append(v)
				else:
					vIsNone = v is None
					defaultOneIsNone = defaultOne[k] is None
					if (vIsNone != defaultOneIsNone) and (vIsNone or defaultOneIsNone or v != defaultOne[k]):
						res[k] = v
		#print(res)
		subMerged = type(res)()
		for k in scheduledSubMerges:
			subMerged[k] = subMerge[k](*scheduledSubMerges[k])
		#print(subMerged)
		res.update(subMerged)
		vals = list(res.values())
		#print(vals)
		return args[0].__class__(*vals)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant