Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/camel-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2047,6 +2047,11 @@
<artifactId>camel-python</artifactId>
<version>4.23.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-python3</artifactId>
<version>4.23.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-qdrant</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions catalog/camel-allcomponents/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,11 @@
<artifactId>camel-python</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-python3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-qdrant</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"CamelLanguageScript": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String or Expression", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The script to execute provided in the header. Takes precedence over script configured on the endpoint.", "constantName": "org.apache.camel.component.language.LanguageConstants#LANGUAGE_SCRIPT" }
},
"properties": {
"languageName": { "index": 0, "kind": "path", "displayName": "Language Name", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "java.lang.String", "enum": [ "bean", "constant", "csimple", "datasonnet", "exchangeProperty", "file", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets the name of the language to use" },
"languageName": { "index": 0, "kind": "path", "displayName": "Language Name", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "java.lang.String", "enum": [ "bean", "constant", "csimple", "datasonnet", "exchangeProperty", "file", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "mvel", "ognl", "python", "python3", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets the name of the language to use" },
"resourceUri": { "index": 1, "kind": "path", "displayName": "Resource Uri", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "description": "Path to the resource, or a reference to lookup a bean in the Registry to use as the resource" },
"allowContextMapAll": { "index": 2, "kind": "parameter", "displayName": "Allow Context Map All", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets whether the context map should allow access to all details. By default only the message body and headers can be accessed. This option can be enabled for full access to the current Exchange and CamelContext. Doing so impose a potential security risk as this opens access to the full power of CamelContext API." },
"allowTemplateFromHeader": { "index": 3, "kind": "parameter", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ publish-subscribe-channel
pubnub-component
pulsar-component
python-language
python3-language
qdrant-component
quartz-component
quickfix-component
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
= Python 3 Language
:doctitle: Python 3
:shortname: python3
:artifactid: camel-python3
:description: Evaluates a Python 3 expression
:since: 4.23
:supportlevel: Preview
:tabs-sync-option:

*Since Camel {since}*

Camel allows https://www.graalvm.org/python/[Python 3] (GraalPy) to be
used as an xref:manual::expression.adoc[Expression] or xref:manual::predicate.adoc[Predicate]
in Camel routes.

This language is distinct from xref:python-language.adoc[Python], which uses Jython and is limited to Python 2.7.

For example, you can use Python 3 in a xref:manual::predicate.adoc[Predicate]
with the xref:eips:choice-eip.adoc[Content-Based Router] EIP.

== Python 3 Options

// language options: START
include::partial$language-options.adoc[]
// language options: END

== Variables

The following variables are bound by default:

[width="100%",cols="10%,10%,80%",options="header",]
|=======================================================================
|Variable |Type |Description
|body |Object |the message body
|headers |Map |the message headers
|properties |Map |the exchange properties
|exchangeId |String |the exchange id
|=======================================================================

By default, Python can index Java maps and lists (for example `headers['foo']`)
but cannot invoke methods on host objects.

`message`, `exchange`, and `context` are not bound in default mode. Scripts that
refer to them raise a Python `NameError`. Those variables are available only when
you opt in to trusted host access, as described in <<_trusted_host_access>>.

== Security

The default GraalPy context does **not** use `HostAccess.ALL` or `allowAllAccess(true)`.
Host method calls on bound objects are denied unless you opt in.

`SandboxPolicy.CONSTRAINED` was evaluated and is **not** the default: it rejects the Java
`Map` host access needed for `headers['foo']` and requires redirecting stdout/stderr on both
the engine and the context. Primitive-only scripts can use it in a custom `Python3Language`.

=== Trusted host access

To allow Python to call public methods on host objects, and to expose Camel host
objects as variables, bind a language created with `createWithHostAccess()` before
the first usage.

This is a trusted host-access mode, **not** a sandbox. `HostAccess.ALL` lets Python
call public methods and fields on bound Java objects. It does **not** enable
`allowAllAccess`, Java class lookup, host IO, or process creation. Use it only when
you trust the scripts.

In this mode the default variables above remain available, plus:

[width="100%",cols="10%,10%,80%",options="header",]
|=======================================================================
|Variable |Type |Description
|message |Message |the message
|exchange |Exchange |the Exchange
|context |CamelContext |the CamelContext
|=======================================================================

[source,java]
----
Python3Language python3 = Python3Language.createWithHostAccess();
camelContext.getRegistry().bind("python3", python3);
----

== Usage

[source,java]
----
import static org.apache.camel.language.python3.Python3Language.python3;

public class MyRouteBuilder extends RouteBuilder {
@Override
public void configure() {
from("direct:start")
.choice()
.when().python3("body == 'Hello'").to("mock:hello")
.otherwise().to("mock:other");
}
}
----

Python 3 syntax is supported, including f-strings:

[source,python]
----
f'Hello {body}'
----

== Dependencies

To use Python 3 in your Camel routes, you need to add the dependency on
*camel-python3*, which implements the Python 3 language with GraalPy.

The GraalPy runtime (language + standard library + Truffle) is large, on the order of
100+ MB of JARs. That is expected for embedding CPython-compatible Python 3 on the JVM.

If you use Maven, you could add the following to your `pom.xml`,
substituting the version number for the latest release.

[source,xml]
----
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-python3</artifactId>
<version>x.x.x</version>
</dependency>
----

GraalPy 25.x embeds CPython 3.12. On JDK 17 it typically runs in interpreter-only mode;
on JDK 21+ and on a GraalVM JDK it can use the optimizing runtime. On JDK 24+ you may
need `--enable-native-access=ALL-UNNAMED` (the unit tests already set this).
GraalPy is skipped on `s390x` and `ppc64le` in the same way as camel-javascript.

GraalPy is licensed under MIT, the Python Software Foundation License, and the Universal
Permissive License (UPL), which are ASF Category A licenses.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jsonpath
mvel
ognl
python
python3
ref
simple
spel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"language": {
"kind": "language",
"name": "python3",
"title": "Python 3",
"description": "Evaluates a Python 3 expression",
"deprecated": false,
"firstVersion": "4.23.0",
"label": "language,python",
"javaType": "org.apache.camel.language.python3.Python3Language",
"supportLevel": "Preview",
"groupId": "org.apache.camel",
"artifactId": "camel-python3",
"version": "4.23.0-SNAPSHOT",
"modelName": "python3",
"modelJavaType": "org.apache.camel.model.language.Python3Expression"
},
"properties": {
"id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." },
"expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." },
"resultType": { "index": 2, "kind": "attribute", "displayName": "Result Type", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The class of the result type (type from output)." },
"trim": { "index": 3, "kind": "attribute", "displayName": "Trim", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the source code to remove leading and trailing whitespaces and line breaks." }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ propertyExpression
protobuf
put
python
python3
randomLoadBalancer
recipientList
redeliveryPolicy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
"note": { "index": 1, "kind": "attribute", "displayName": "Note", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The note for this node" },
"description": { "index": 2, "kind": "attribute", "displayName": "Description", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The description for this node" },
"disabled": { "index": 3, "kind": "attribute", "displayName": "Disabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
"correlationExpression": { "index": 4, "kind": "expression", "displayName": "Correlation Expression", "group": "common", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "description": "The expression used to calculate the correlation key to use for aggregation. Exchanges with the same correlation key are aggregated together. If the correlation key cannot be evaluated an Exception is thrown." },
"completionPredicate": { "index": 5, "kind": "expression", "displayName": "Completion Predicate", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "asPredicate": true, "description": "A predicate to indicate when an aggregated exchange is complete. If not specified and the AggregationStrategy implements Predicate, it will be used as the completionPredicate." },
"completionTimeoutExpression": { "index": 6, "kind": "expression", "displayName": "Completion Timeout Expression", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "description": "Time in millis that an aggregated exchange should be inactive before its complete (timeout), evaluated as an expression allowing dynamic timeout values." },
"completionSizeExpression": { "index": 7, "kind": "expression", "displayName": "Completion Size Expression", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "description": "Number of messages aggregated before the aggregation is complete, evaluated as an expression allowing dynamic size values." },
"correlationExpression": { "index": 4, "kind": "expression", "displayName": "Correlation Expression", "group": "common", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "python3", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "description": "The expression used to calculate the correlation key to use for aggregation. Exchanges with the same correlation key are aggregated together. If the correlation key cannot be evaluated an Exception is thrown." },
"completionPredicate": { "index": 5, "kind": "expression", "displayName": "Completion Predicate", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "python3", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "asPredicate": true, "description": "A predicate to indicate when an aggregated exchange is complete. If not specified and the AggregationStrategy implements Predicate, it will be used as the completionPredicate." },
"completionTimeoutExpression": { "index": 6, "kind": "expression", "displayName": "Completion Timeout Expression", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "python3", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "description": "Time in millis that an aggregated exchange should be inactive before its complete (timeout), evaluated as an expression allowing dynamic timeout values." },
"completionSizeExpression": { "index": 7, "kind": "expression", "displayName": "Completion Size Expression", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "jactl", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "python3", "ref", "simple", "spel", "tokenize", "variable", "wasm", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "description": "Number of messages aggregated before the aggregation is complete, evaluated as an expression allowing dynamic size values." },
"optimisticLockRetryPolicy": { "index": 8, "kind": "element", "displayName": "Optimistic Lock Retry Policy", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.OptimisticLockRetryPolicyDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Configures retry settings when using optimistic locking." },
"parallelProcessing": { "index": 9, "kind": "attribute", "displayName": "Parallel Processing", "group": "common", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When completed exchanges are sent out of the aggregator, this option indicates whether Camel should use a thread pool with multiple threads for concurrency." },
"optimisticLocking": { "index": 10, "kind": "attribute", "displayName": "Optimistic Locking", "group": "common", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Turns on optimistic locking, which requires the aggregation repository to implement OptimisticLockingAggregationRepository." },
Expand Down
Loading