# Create a scripting function with optional parameters in the SDK

**URL:** <https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228>\
**Category:** Module Development\
**Tags:** sdk\
**Created:** [July 27, 2021, 8:12am UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228 "2021-07-27T08:12:42Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![mehdi.bouzit1](https://avatars.discourse-cdn.com/v4/letter/m/9de053/32.png) [@mehdi.bouzit1](https://forum.inductiveautomation.com/u/mehdi.bouzit1)\
**Post date:** [July 27, 2021, 8:12am UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/1 "2021-07-27T08:12:42Z")

</div>

I’m trying to develop a scripting function with the java SDK and I would like to implement optional arguments. Is there an elegant way to achieve this? Typically, how are out of the box scripting functions with optional arguments programmed by IA ?  
Thanks in advance.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 27, 2021, 12:29pm UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/2 "2021-07-27T12:29:12Z")

</div>

Here’s a snippet from my Ethernet/IP module that should get you started:

```auto
	@KeywordArgs(names = { "name", "slot", "id" }, types = { String.class, Integer.class, Long.class })
	static public PyObject getDevice(PyObject[] args, String[] keywords) {
		ArgParser ap = new ArgParser("getDevice", args, keywords, new String[] { "name", "slot", "id" });
		String name = ap.getString(0, null);
		int slot = ap.getInt(1, -1);
		long id = (Long) Py.tojava(ap.getPyObject(2, new PyLong(0L)), Long.class);

```

---

<div class="post-metadata">

**Author:** ![mehdi.bouzit1](https://avatars.discourse-cdn.com/v4/letter/m/9de053/32.png) [@mehdi.bouzit1](https://forum.inductiveautomation.com/u/mehdi.bouzit1)\
**Post date:** [July 27, 2021, 12:52pm UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/3 "2021-07-27T12:52:01Z")

</div>

Thanks for your answer. I understand your code and it is exactly what I’m looking for.  
That being said, my function’s declaration in the **AbstractScriptModule** file should then look like:

```auto
@Override
@ScriptFunction(docBundlePrefix = "AbstractScriptModule")
public String getRawData(PyObject[] args, String[] keywords) {
        return getRawDataImpl(args, keywords);
}
protected abstract String getRawDataImpl(PyObject[] args, String[] keywords);

```

So there won’t be any input parameter with the **@ScriptArg** annotation like shown in the examples right? Is that ok?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 27, 2021, 1:39pm UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/4 "2021-07-27T13:39:37Z")

</div>

I’m not sure how to combine the `@KeywordArgs` annotation with the `@ScriptFunction` annotation. I don’t use the latter. I have a custom doc provider with similar functionality.

---

<div class="post-metadata">

**Author:** ![KathyApplebaum](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kathyapplebaum/32/7261_2.png) [@KathyApplebaum](https://forum.inductiveautomation.com/u/KathyApplebaum)\
**Post date:** [July 27, 2021, 2:36pm UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/5 "2021-07-27T14:36:35Z")

</div>

It’s perfectly fine to have both the `@ScriptFunction` and `@KeywordArgs` annotations on the same function. We do this all the time internally.

To answer @mehdi.bouzit1 's question, with an example from our code…

The superclass looks like this:  
` Dataset queryAgentStatus(PyObject[] pyArgs, String[] keywords);`  
(no annotations at all)

One of the subclasses looks like this:

```auto
    @ScriptFunction(docBundlePrefix = "EAMScriptingFunctions")
    @KeywordArgs(names = {"groupIds", "agentIds", "isConnected"}, types = {String[].class,
        String[].class, Boolean.class})
    public Dataset queryAgentStatus(PyObject[] pyArgs, String[] keywords) {
       // some code here
}

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [July 27, 2021, 3:24pm UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/6 "2021-07-27T15:24:38Z")

</div>

> [@pturmel](#):
>
> `ArgParser ap = new ArgParser("getDevice", args, keywords, new String[] { "name", "slot", "id" });`

If you're on newer 8.0.X+ SDK, you could try out `PyArgParser` in common - it's much more reasonable about automatic casting (imo) than `PyArgumentMap`.

> [@pturmel](#):
>
> `long id = (Long) Py.tojava(ap.getPyObject(2, new PyLong(0L)), Long.class);`

Also, `TypeUtilities.pyToJava` is nice if you could have someone pass very large numbers - at some point, Jython switches to BigInteger/BigDecimal as the return from `tojava`, which the TypeUtilities method will automatically unpack.

As an aside for @mehdi.bouzit1 - while the `PyObject[], String[]` signature is definitely preferred, Jython also negotiates 'optional' arguments just fine if you do plain Java overloads - sometimes that can be easier for functions that only accept a couple arguments, since you also get automatic Jython type coercion and don't have to worry about unpacking the `pyArgs` array.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 27, 2021, 4:32pm UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/7 "2021-07-27T16:32:04Z")

</div>

> [@paul-griffith](#):
>
> If you’re on newer 8.0.X+ SDK

Most of my projects are dual 7.9/8.1. I don't actually use your SDK, per se.

> [@paul-griffith](#):
>
> Jython switches to BigInteger/BigDecimal as the return from `tojava` , which the TypeUtilities method will automatically unpack.

Ah, handy. Will probably use next time.

> [@paul-griffith](#):
>
> Jython also negotiates ‘optional’ arguments just fine if you do plain Java overloads - sometimes that can be easier for functions that only accept a couple arguments, since you also get automatic Jython type coercion and don’t have to worry about unpacking the `pyArgs` array.

I do this most of the time.

---

<div class="post-metadata">

**Author:** ![mehdi.bouzit1](https://avatars.discourse-cdn.com/v4/letter/m/9de053/32.png) [@mehdi.bouzit1](https://forum.inductiveautomation.com/u/mehdi.bouzit1)\
**Post date:** [July 28, 2021, 8:04am UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/8 "2021-07-28T08:04:16Z")

</div>

Thanks everyone, I’ve been able to successfully create my method with optional arguments with the **ArgParser** function.

That being said, I tried to use **`PyArgParser`** (probably the wrong way) as follows:

```auto
@Override
@ScriptFunction(docBundlePrefix = "AbstractScriptModule")
@KeywordArgs(names = { "arg1", "arg2", "arg3", ... },
                          types = { String.class, String.class,String.class,String.class,String.class, ...})
    protected String getRawDataImpl(PyObject[] args, String[] keywords) throws IOException{
        PyArgParser pap = new PyArgParser(args, keywords);
        String[] cmdArray = new String[16];
        cmdArray[0] = pap.arg(0,null).toString();
        cmdArray[1] = pap.arg(1,null).toString();
        cmdArray[2] = pap.arg(2,null).toString();
        ...

```

and ended having a **NullPointerException** if I hit an argument I didn’t provide.

Furthermore, on the designer, if I hit Ctrl+Space to display my function’s tooltip, it’s displaying something like:

```auto
getRawData(PyObject[], String[]) → String
Get raw data from the VSE.
Parameters:
  PyObject[] - No description provided.
  String[] - No description provided.
Returns:
    No return value.

```

Is there any way to display a tooltip with all my arguments ?  
Thank you.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [July 28, 2021, 4:33pm UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/9 "2021-07-28T16:33:12Z")

</div>

> [@mehdi.bouzit1](#):
>
> That being said, I tried to use **`PyArgParser`** (probably the wrong way) as follows:

I'm pretty sure you're using the wrong class; there's a `PyArgParser` in Jython, but the one to use is  
`com.inductiveautomation.ignition.common.script.PyArgParser`.

> [@mehdi.bouzit1](#):
>
> Furthermore, on the designer, if I hit Ctrl+Space to display my function’s tooltip, it’s displaying something like:

Do you have each parameter identified in your .properties file?

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/a/6a0f6a5285a419e41c5665249ddc4c5a27965a03.png)  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/d/dd5ed21b7f9a523c5c65707021f71802a6ab2a5e.png)

---

<div class="post-metadata">

**Author:** ![mehdi.bouzit1](https://avatars.discourse-cdn.com/v4/letter/m/9de053/32.png) [@mehdi.bouzit1](https://forum.inductiveautomation.com/u/mehdi.bouzit1)\
**Post date:** [July 29, 2021, 8:29am UTC](https://forum.inductiveautomation.com/t/create-a-scripting-function-with-optional-parameters-in-the-sdk/49228/10 "2021-07-29T08:29:29Z")

</div>

> [@paul-griffith](#):
>
> I’m pretty sure you’re using the wrong class

You are right, I was using `com.ziclix.python.sql.util.PyArgParser`...  
I had to explicitly add:

```auto
import com.inductiveautomation.ignition.common.script.PyArgParser;

```

and import the maven dependancy.

I used this function as follows:

```auto
@Override
@ScriptFunction(docBundlePrefix = "AbstractScriptModule")
@KeywordArgs(names = { "arg1", "arg2",...},types = { String.class, String.class,String.class,...})
    protected String getRawDataImpl(PyObject[] args, String[] keywords) throws IOException{       
        PyArgParser pap = PyArgParser.parseArgs(args,keywords,new String[] { "arg1", "arg2",...},
                new Class[]{String.class, String.class,...},"getRawDataImpl");
       // Get the parameters
       pap.getString("arg1").orElse("default value 1");
       pap.getString("arg2").orElse("default value 2");
       ...

```

As for the .properties file issue, I used the SDK example philosophy and used a myMethod and myMethodImpl, as used with the multiply method. My .properties file was linked to getRawData, so I just had to add a `@KeywordArgs(names = { "arg1", "arg2",...},types = { String.class, String.class,String.class,...})` on top of the getRawData declaration in the AbstractScriptModule file.

Thank you for your support !
